Java多线程 - 利用Callable或CompletableFuture实现多线程异步任务执行

news/2024/4/19 1:32:58/文章来源:https://blog.csdn.net/qq_42764468/article/details/129127729

文章目录

      • 1. Callable接口源码
      • 2. Future接口的源码
      • 3. RunnableFuture接口和FutureTask实现类
      • 4. 利用线程池和Callable接口实现异步执行任务
      • 5. 利用CompleteFutable实现多线程异步任务执行

1. Callable接口源码

@FunctionalInterface
public interface Callable<V> {// 这个call()方法有返回值,且声明了受检异常,可以直接抛出Exception异常V call() throws Exception;
}

2. Future接口的源码

public interface Future<V> {// 取消异步执行中的任务boolean cancel(boolean mayInterruptIfRunning);boolean isCancelled();// 判断异步任务是否执行成功boolean isDone();// 获取异步任务完成后的结果V get() throws InterruptedException, ExecutionException;V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
}

Future接口中的方法:

get():获取异步任务执行的结果。注意,这个方法的调用是阻塞性的。如果异步任务没有执行完成,异步结果获取线程(调用线程)会一直被阻塞,一直阻塞到异步任务执行完成,其异步结果返回给调用线程。

get(Long timeout,TimeUnit unit):设置时限,(调用线程)阻塞性地获取异步任务执行的结果。该方法的调用也是阻塞性的,但是结果获取线程(调用线程)会有一个阻塞时长限制,不会无限制地阻塞和等待,如果其阻塞时间超过设定的timeout时间,该方法将抛出异常,调用线程可捕获此异常。

boolean isDone():获取异步任务的执行状态。如果任务执行结束,就返回true。

boolean isCancelled():获取异步任务的取消状态。如果任务完成前被取消,就返回true。

boolean cancel(boolean mayInterruptRunning):取消异步任务的执行。

3. RunnableFuture接口和FutureTask实现类

如何使用Callable接口创建线程呢?

public interface RunnableFuture<V> extends Runnable, Future<V> {void run();
}

RunnableFuture只是一个接口,无法直接创建对象,如果需要创建对象,就需用到它的实现类——FutureTask。

public class FutureTask<V> implements RunnableFuture<V> {private Callable<V> callable;// 构造方法public FutureTask(Callable<V> callable) {if (callable == null) throw new NullPointerException();// callable实例属性需要在FutureTask实例构造时进行初始化this.callable = callable;this.state = NEW;       }
}

RunnableFuture接口实现了2个目标:

(1) RunnableFuture通过继承Runnable接口,从而保证了其实例可以作为Thread线程实例的target目标;
(2) RunnableFuture通过继承Future接口,从而保证了可以获取未来的异步执行结果。

首先,通过实现Runnable接口的方式创建一个异步执行任务:

public class CallableTaskDemo implements Callable {// call()方法有返回值,并且可以抛出Exception异常@Overridepublic String call() throws Exception {System.out.println("实现Callable接口来编写异步执行任务");Thread.sleep(1000);return "返回线程执行结果";}
}

方式1:通过Thread类创建线程执行异步任务

public class CallableDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {// 创建异步执任务实例Callable callable = new CallableTaskDemo();// 初始化callable实例属性FutureTask futureTask = new FutureTask(callable);// 创建线程执行异步任务Thread thread = new Thread(futureTask);thread.start();System.out.println("获取异步执行任务结果:"+futureTask.get());// 获取异步执行任务结果:返回线程执行结果}
}

方式2:通过线程池创建线程,并提交异步执行任务:

public class CallableDemo2 {// 通过线程池创建3个线程private static ExecutorService executorService = Executors.newFixedThreadPool(3);public static void main(String[] args) throws ExecutionException, InterruptedException {// 通过线程池的submit()方法提交异步执行任务,有返回结果Future submit = executorService.submit(new CallableTaskDemo());// 获取返回结果System.out.println(submit.get());}
}

对于Calleble来说,Future和FutureTask均可以用来获取任务执行结果,不过Future是个接口,FutureTask是Future的具体实现,而且FutureTask还间接实现了Runnable接口,也就是说FutureTask可以作为Runnable任务提交给线程池。

4. 利用线程池和Callable接口实现异步执行任务

1、定义3个线程执行任务:

public class FirstCallableTask implements Callable<String> {// call()方法有返回值,并且可以抛出Exception异常@Overridepublic String call() throws Exception {System.out.println("实现 First Callable接口来编写异步执行任务");Thread.sleep(1000);return "返回 First Callable 接口线程执行结果";}
}public class SecondCallableTask implements Callable<String> {@Overridepublic String call() throws Exception {System.out.println("实现 Second Callable接口来编写异步执行任务");Thread.sleep(1000);return "返回 Second Callable 接口线程执行结果";}
}public class ThirdCallableTask implements Callable<String> {@Overridepublic String call() throws Exception {System.out.println("实现 Third Callable接口来编写异步执行任务");Thread.sleep(1000);return "返回 Third Callable 接口线程执行结果";}
}

2、定义线程池提交线程任务:

@Service
@Slf4j
public class BeanLoadService implements ApplicationContextAware, ApplicationListener<ContextStoppedEvent> {private ApplicationContext applicationContext;// 定义一个线程池private static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(5,7,4,TimeUnit.SECONDS,new LinkedBlockingQueue<>(),new ThreadFactoryBuilder().setNamePrefix(BeanLoadService.class.getSimpleName() + "-pool-%d").setDaemon(true).build(),new ThreadPoolExecutor.DiscardOldestPolicy());public Map<String,Integer> richInfo() {Callable<String> firstCallable = new FirstCallableTask();Callable<String> secondCallable = new SecondCallableTask();Callable<String> thirdCallable = new ThirdCallableTask();List<Callable<String>> callableList = new ArrayList<>();callableList.add(firstCallable);callableList.add(secondCallable);callableList.add(thirdCallable);try {List<Future<String>> futures = THREAD_POOL_EXECUTOR.invokeAll(callableList, 1, TimeUnit.MINUTES);for (Future<String> future : futures) {try {System.out.println(future.get());} catch (Exception e) {log.warn("incident delay data,future get warn", e);}}} catch (Exception e) {log.warn("incident delay data warn ", e);}}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}@Overridepublic void onApplicationEvent(ContextStoppedEvent contextStoppedEvent) {try {THREAD_POOL_EXECUTOR.shutdown();} catch (Exception e) {log.error("停止线程池失败", e);}}
}

3、测试:

@SpringBootTest
@RunWith(SpringRunner.class)
public class BeanLoadServiceTest {@Autowiredprivate BeanLoadService beanLoadService;@Testpublic void test(){beanLoadService.richInfo();}
}

实现 First Callable接口来编写异步执行任务
实现 Second Callable接口来编写异步执行任务
实现 Third Callable接口来编写异步执行任务
返回 First Callable 接口线程执行结果
返回 Second Callable 接口线程执行结果
返回 Third Callable 接口线程执行结果

4、定义线程池提交线程任务(内部类):

@Service
@Slf4j
public class BeanLoadService implements ApplicationContextAware, ApplicationListener<ContextStoppedEvent> {private ApplicationContext applicationContext;// 定义一个线程池private static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(5,7,4,TimeUnit.SECONDS,new LinkedBlockingQueue<>(),new ThreadFactoryBuilder().setNamePrefix(BeanLoadService.class.getSimpleName() + "-pool-%d").setDaemon(true).build(),new ThreadPoolExecutor.DiscardOldestPolicy());public Map<String,Integer> richInfo() {Callable<String> firstCallable = getFirstCallable();Callable<String> secondCallable = getSecondCallable();Callable<String> thirdCallable = getThirdCallable();List<Callable<String>> callableList = new ArrayList<>();callableList.add(firstCallable);callableList.add(secondCallable);callableList.add(thirdCallable);try {List<Future<String>> futures = THREAD_POOL_EXECUTOR.invokeAll(callableList, 1, TimeUnit.MINUTES);for (Future<String> future : futures) {try {System.out.println(future.get());} catch (Exception e) {log.warn("incident delay data,future get warn", e);}}} catch (Exception e) {log.warn("incident delay data warn ", e);}}private Callable<String> getThirdCallable() {Callable<String> callable = new Callable<String>() {@Overridepublic String call() throws Exception {System.out.println("实现 Third Callable接口来编写异步执行任务");Thread.sleep(1000);return "返回 Third Callable 接口线程执行结果";}};}private Callable<String> getSecondCallable() {Callable<String> callable = new Callable<String>() {@Overridepublic String call() throws Exception {System.out.println("实现 Second Callable接口来编写异步执行任务");Thread.sleep(1000);return "返回 Second Callable 接口线程执行结果";};};return callable;}private Callable<String> getFirstCallable() {Callable<String> callable = new Callable<String>() {@Overridepublic String call() throws Exception {System.out.println("实现 First Callable接口来编写异步执行任务");Thread.sleep(1000);return "返回 First Callable 接口线程执行结果";}};return callable;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}@Overridepublic void onApplicationEvent(ContextStoppedEvent contextStoppedEvent) {try {THREAD_POOL_EXECUTOR.shutdown();} catch (Exception e) {log.error("停止线程池失败", e);}}
}

5、定义线程池提交任务:(Java 8)

@Service
@Slf4j
public class BeanLoadService implements ApplicationContextAware, ApplicationListener<ContextStoppedEvent> {private ApplicationContext applicationContext;// 定义一个线程池private static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(5,7,4,TimeUnit.SECONDS,new LinkedBlockingQueue<>(),new ThreadFactoryBuilder().setNamePrefix(BeanLoadService.class.getSimpleName() + "-pool-%d").setDaemon(true).build(),new ThreadPoolExecutor.DiscardOldestPolicy());public void richInfo() {Callable<String> firstCallable = getFirstCallable();Callable<String> secondCallable = getSecondCallable();Callable<String> thirdCallable = getThirdCallable();List<Callable<String>> callableList = new ArrayList<>();callableList.add(firstCallable);callableList.add(secondCallable);callableList.add(thirdCallable);try {List<Future<String>> futures = THREAD_POOL_EXECUTOR.invokeAll(callableList, 1, TimeUnit.MINUTES);for (Future<String> future : futures) {try {System.out.println(future.get());} catch (Exception e) {log.warn("incident delay data,future get warn", e);}}} catch (Exception e) {log.warn("incident delay data warn ", e);}}private Callable<String> getThirdCallable() {Callable<String> callable = ()->{System.out.println("实现 Third Callable接口来编写异步执行任务");Thread.sleep(1000);return "返回 Third Callable 接口线程执行结果";};return callable;}private Callable<String> getSecondCallable() {Callable<String> callable = ()-> {System.out.println("实现 Second Callable接口来编写异步执行任务");Thread.sleep(1000);return "返回 Second Callable 接口线程执行结果";};return callable;}private Callable<String> getFirstCallable() {Callable<String> callable  = () -> {System.out.println("实现 First Callable接口来编写异步执行任务");Thread.sleep(1000);return "返回 First Callable 接口线程执行结果";};return callable;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}@Overridepublic void onApplicationEvent(ContextStoppedEvent contextStoppedEvent) {try {THREAD_POOL_EXECUTOR.shutdown();} catch (Exception e) {log.error("停止线程池失败", e);}}
}

5. 利用CompleteFutable实现多线程异步任务执行

在 Java 8 中, 新增加了一个包含 50 个方法左右的类: CompletableFuture, 提供了非常强大的Future 的扩展功能, 可以帮助我们简化异步编程的复杂性, 提供了函数式编程的能力, 可以通过回调的方式处理计算结果, 并且提供了转换和组合 CompletableFuture 的方法。CompletableFuture 类实现了 Future 接口, 所以你还是可以像以前一样通过get方法阻塞或者轮询的方式获得结果, 但是这种方式不推荐使用。

CompletableFuture 和 FutureTask 同属于 Future 接口的实现类, 都可以获取线程的执行结果。

CompletableFuture 提供了四个静态方法来创建一个异步操作 :

runXxxx 都是没有返回结果的, supplyXxx 都是可以获取返回结果的,可以传入自定义的线程池, 否则就用默认的线程池 。

//子任务包装一个Runnable实例,并调用ForkJoinPool.commonPool()线程池来执行
public static CompletableFuture<Void> runAsync(Runnable runnable)//子任务包装一个Runnable实例,并调用指定的executor线程池来执行
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)//子任务包装一个Supplier实例,并调用ForkJoinPool.commonPool()线程池来执行
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)//子任务包装一个Supplier实例,并使用指定的executor线程池来执行
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
@Service
@Slf4j
public class BeanLoadService {public void getInfo() {List<CompletableFuture<String>> futures = new ArrayList<>();CompletableFuture<String> firstFuture = CompletableFuture.supplyAsync(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}return "返回 First Callable 接口线程执行结果";});futures.add(firstFuture);CompletableFuture<String> secondFuture = CompletableFuture.supplyAsync(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}return "返回 Second Callable 接口线程执行结果";});futures.add(secondFuture);CompletableFuture<String> thirdFuture = CompletableFuture.supplyAsync(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}return "返回 Third Callable 接口线程执行结果";});futures.add(thirdFuture);try {futures.forEach(future -> {try {System.out.println(future.get());} catch (Exception e) {log.warn("incident delay data,future get warn", e);}});} catch (Exception e) {log.warn("incident delay data warn ", e);}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_71812.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

企业微信的聊天机器人来了,免费下载(Python版)

大家好&#xff0c;这里是程序员晚枫&#xff0c;个人网址&#xff1a;python-office.com 上次分享了微信机器人的视频以后&#xff0c;视频下面有一个热门评论&#xff1a; 什么时候开发企业版微信机器人&#xff1f;自动回复、自动群发等等~ 在经历了一段时间的查找和开发以…

【基础算法】之 冒泡排序优化

冒泡排序思想基本思想: 冒泡排序&#xff0c;类似于水中冒泡&#xff0c;较大的数沉下去&#xff0c;较小的数慢慢冒起来&#xff08;假设从小到大&#xff09;&#xff0c;即为较大的数慢慢往后排&#xff0c;较小的数慢慢往前排。直观表达&#xff0c;每一趟遍历&#xff0c;…

Docker----------day3

常规安装大体步骤 1.安装tomcat 1.查找tomcat docker search tomcat2.拉取tomcat docker pull tomcat3.docker images查看是否有拉取到的tomcat 4.使用tomcat镜像创建容器实例(也叫运行镜像) docker run -it -p 8080:8080 tomcat5.新版tomcat把webapps.dist目录换成webapp…

【大数据离线开发】7.4 HBase数据保存和过滤器

7.4 数据保存的过程 注意&#xff1a;数据的存储&#xff0c;都需要注意Region的分裂 HDFS&#xff1a;数据的平衡 ——> 数据的移动&#xff08;拷贝&#xff09;HBase&#xff1a;数据越来越多 ——> Region的分裂 ——> 数据的移动&#xff08;拷贝&#xff09; …

清理bib文件(删除重复项,仅保留tex中引用的条目)

在写latex文件的过程中&#xff0c;经常会遇到添加了一堆文献的bibtex到bib文件中&#xff0c;有时候文章一长同一篇文献用不同的cite-key引用了多次&#xff0c;同时也会有一些文献最后并没被正文引用&#xff0c;这就需要对bib文件进行清理。 删除重复项 可以用JabRef 在J…

经理与员工工资关系-课后程序(JAVA基础案例教程-黑马程序员编著-第四章-课后作业)

【案例4-6】经理与员工工资案例&#xff08;利用多态实现&#xff09; 欢迎点赞关注收藏 【案例介绍】 案例描述 某公司的人员分为员工和经理两种&#xff0c;但经理也属于员工中的一种&#xff0c;公司的人员都有自己的姓名和地址&#xff0c;员工和经理都有自己的工号、工…

不同投票需要的不同上传方式outlook 投票功能怎么设置投票 html5

“艺空间手造坊”网络评选投_投票方式的选择_免费图文教学投票教学关于微信投票&#xff0c;我们现在用的最多的就是小程序投票&#xff0c;今天的网络投票&#xff0c;在这里会教大家如何用“活动星投票”小程序来进行投票。我们现在要以“艺空间手造坊”为主题进行一次投票活…

AcWing1015.摘花生

AcWing 1015. 摘花生Hello Kitty想摘点花生送给她喜欢的米老鼠。她来到一片有网格状道路的矩形花生地(如下图)&#xff0c;从西北角进去&#xff0c;东南角出来。地里每个道路的交叉点上都有种着一株花生苗&#xff0c;上面有若干颗花生&#xff0c;经过一株花生苗就能摘走该它…

Java并发知识点

文章目录1. start()和run()方法的区别&#xff1f;2. volatile关键字的作用&#xff1f;使用volatile能够保证&#xff1a;防止指令重排3. sleep方法和wait方法有什么区别&#xff1f;sleep()方法4. 如何停止一个正在运行的线程&#xff1f;方法一&#xff1a;方法二&#xff1…

多重继承的虚函数表

同一个类,不同对象使用同一张虚函数表 不同类使用不同的虚函数表 子类自己添加的虚函数(非重写),在VS中是将此放在第一个继承类的虚函数表里. #include <iostream> using namespace std;class Father { public:virtual void func1() { cout << "Father::f…

<Linux>vscode搭建Linux远程开发工具

一、下载vscode&#x1f603;可以去vscode的官网下载&#xff0c;不过是外网下载速度较慢提速可以参考&#xff1a;(81条消息) 解决VsCode下载慢问题_vscode下载太慢_wang13679201813的博客-CSDN博客官网&#xff1a;Visual Studio Code - Code Editing. Redefined这里推荐的是…

【数据结构】二叉树的四种遍历

写在前面首先二叉树是一个大家族&#xff0c;这篇文章就讲一讲二叉树的遍历&#xff1a;递归遍历迭代遍历先识概念二叉树的存储结构&#xff0c;可以为顺序存储&#xff0c;即使用数组&#xff1b;也可以为链式存储&#xff0c;即使用链表。我们使用较多的就是链式存储结构&…

Ceres的自动求导实现原理剖析

目录数学原理实现原理总结首先注意数值求导和自动求导在使用的时候的不同之处。 实际上&#xff0c;正是自动求导这个地方使用了类模板&#xff0c;导致它不仅可以传入参数&#xff0c;还可以传入Jet类型的数据&#xff0c;从而实现了参数的雅可比矩阵的计算&#xff0c;完成自…

TPM密钥管理、使用

前面讲过证书相关内容&#xff0c;除了在软件方面有所应用外&#xff0c;在硬件方面也有很多应用。本次讲一下TPM相关的内容。 一、TPM介绍 1.1背景 TCG基于硬件安全的架构是为应对1990s后期日益增多的复杂恶意软件攻击应用而生的。当时以及现在&#xff0c;抵御PC客户端网络…

树状数组(高级数据结构)-蓝桥杯

一、简介树状数组 (Binary Indexed Tree,BIT)&#xff0c;利用数的二进制特征进行检索的一种树状结构。一种真正的高级数据结构&#xff1a; 二分思想、二叉树、位运算、前缀和。高效!代码极其简洁!二、基本应用数列a1,a2,....,an&#xff0c;操作&#xff1a;单点修改&#xf…

详解HashMap

目录 1.hash code 2.数据结构 3.初始化 4.存取 4.1.put 4.2.get 5.迭代 6.扩容 7.JDK1.7版本存在的问题 7.1.性能跌落 7.2.循环链表 8.散列运算 9.扰动函数 1.hash code hash code是使用hash函数运算得到的一个值&#xff0c;是对象的身份证号码&#xff0c;用于…

OpenSumi 是信创开发云的首选

原文作者&#xff1a;行云创新技术总监 邓冰寒 引言 随着云原生应用的日益普及&#xff0c;开发上云也逐步被越来越多的厂商和开发者接受&#xff0c;在这个赛道国内外有不少玩家&#xff0c;国外的 GitHub Codespaces、CodeSandbox&#xff0c;GitPod、亚马逊 Cloud9&#xf…

借力英特尔® Smart Edge,灵雀云 ACP 5G 专网解决方案获得多维度优化加速

近日&#xff0c;灵雀云联合英特尔推出了集成Smart Edge 模块的灵雀云 ACP 5G 专网解决方案&#xff0c;同时共同发布了《借力英特尔 Smart Edge&#xff0c;基于云原生解决方案的灵雀云 ACP 5G 专网版本获得多维度优化加速》白皮书。 得益于云计算技术和 5G 网络的高速发展&am…

Win10 环境 安卓ollvm编译与配置 ndk代码混淆加密

确定你正在使用的ndk版本 查看build.gradle ndkVersion 21.4.7075529 确定你使用的ndk的ollvm版本 C:\Users\Administrator\AppData\Local\Android\Sdk\ndk\21.4.7075529\toolchains\llvm\prebuilt\windows-x86_64\bin\llvm-config.exe --version 9.0.9svn 确定了ollvm版本后…

动手学深度学习(第二版)学习笔记 第二章

官网&#xff1a;http://zh.d2l.ai/ 视频可以去b站找 记录的是个人觉得不太熟的知识 第二章 预备知识 代码地址&#xff1a;d2l-zh/pytorch/chapter_preliminaries 2.1 数据操作 2.1. 数据操作 — 动手学深度学习 2.0.0 documentation 如果只想知道张量中元素的总数&#…