JAVA并发-Future/CompletableFuture
文章目录
- 一、Future
- 1. 什么是Future
- 2. future如何使用
- 2.1 FutureTask 工具类
- 3. Future 接口的局限性
- 二、CompletableFuture
- 1. 什么是CompletableFuture
- 2. 如何使用CompletableFuture
- 3. demo举例
- 3.1 以获取股票价格为例,看看如何使用CompletableFuture
- 3.2 CompletableFuture描述实现串行关系、AND 汇聚关系、 OR汇聚关系
- 三、参考
一、Future
1. 什么是Future
Java 1.5开始,提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。
1、Callable与Runnable
java.lang.Runnable是一个接口,在它里面只声明了一个run()方法,run返回值是void,任务执行完毕后无法返回任何结果
public interface Runnable {public abstract void run();
}
Callable位于java.util.concurrent包下,它也是一个接口,在它里面也只声明了一个方法叫做call(),这是一个泛型接口,call()函数返回的类型就是传递进来的V类型
public interface Callable<V> {V call() throws Exception;
}
2、Future + Callable
Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果.
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;
}
2. future如何使用
业务开发中,我们一般首先要创建正确的线程池,那创建完线程池,我们该如何使用呢?
ThreadPoolExecutor 的 void execute(Runnable command) 方法,利用这个方法虽然可以提交任务,但是却没有办法获取任务的执行结果(execute() 方法没有返回值)。我们又都是需要获取任务的执行结果的。那 ThreadPoolExecutor 是否提供了相关功能呢?必须的,这么重要的功能当然需要提供了。
**Java 通过 ThreadPoolExecutor 提供的 3 个 submit() 方法和 1 个 FutureTask 工具类来支持获得任务执行结果的需求。**下面我们先来介绍这 3 个 submit() 方法,这 3 个方法的方法签名如下。
// 提交Runnable任务
Future<?> submit(Runnable task);
// 提交Callable任务
<T> Future<T> submit(Callable<T> task);
// 提交Runnable任务及结果引用
<T> Future<T> submit(Runnable task, T result);
你会发现它们的返回值都是 Future 接口,Future 接口有 5 个方法,我都列在下面了,它们分别是取消任务的方法 cancel()、判断任务是否已取消的方法 isCancelled()、判断任务是否已结束的方法 isDone()以及2 个获得任务执行结果的 get() 和 get(timeout, unit),其中最后一个 get(timeout, unit) 支持超时机制。通过 Future 接口的这 5 个方法你会发现,我们提交的任务不但能够获取任务执行结果,还可以取消任务。不过需要注意的是:这两个 get() 方法都是阻塞式的,如果被调用的时候,任务还没有执行完,那么调用 get() 方法的线程会阻塞,直到任务执行完才会被唤醒。
- 提交 Runnable 任务 submit(Runnable task) :这个方法的参数是一个 Runnable 接口,Runnable 接口的 run() 方法是没有返回值的,所以 submit(Runnable task) 这个方法返回的 Future 仅可以用来断言任务已经结束了,类似于 Thread.join()。
- 提交 Callable 任务 submit(Callable task):这个方法的参数是一个 Callable 接口,它只有一个 call() 方法,并且这个方法是有返回值的,所以这个方法返回的 Future 对象可以通过调用其 get() 方法来获取任务的执行结果。
- 提交 Runnable 任务及结果引用 submit(Runnable task, T result):这个方法很有意思,假设这个方法返回的 Future 对象是 f,**f.get() 的返回值就是传给 submit() 方法的参数 result。**这个方法该怎么用呢?下面这段示例代码展示了它的经典用法。需要你注意的是 Runnable 接口的实现类 Task 声明了一个有参构造函数 Task(Result r) ,创建 Task 对象的时候传入了 result 对象,这样就能在类 Task 的 run() 方法中对 result 进行各种操作了。result 相当于主线程和子线程之间的桥梁,通过它主子线程可以共享数据。
2.1 FutureTask 工具类
FutureTask 工具类。前面我们提到的 Future 是一个接口,而 FutureTask 是一个实实在在的工具类,这个工具类有两个构造函数
FutureTask(Callable<V> callable);
FutureTask(Runnable runnable, V result);
那如何使用 FutureTask 呢?其实很简单,**FutureTask 实现了 Runnable 和 Future 接口,由于实现了 Runnable 接口,所以可以将 FutureTask 对象作为任务提交给 ThreadPoolExecutor 去执行,也可以直接被 Thread 执行;又因为实现了 Future 接口,所以也能用来获得任务的执行结果。**下面的示例代码是将 FutureTask 对象提交给 ThreadPoolExecutor 去执行。
// 创建FutureTask
FutureTask<Integer> futureTask = new FutureTask<>(()-> 1+2);
// 创建线程池
ExecutorService es = Executors.newCachedThreadPool();
// 提交FutureTask
es.submit(futureTask);
// 获取计算结果
Integer result = futureTask.get();
3. Future 接口的局限性
了解了Future的使用,这里就要谈谈Future的局限性。Future很难直接表述多个Future 结果之间的依赖性,开发中,我们经常需要达成以下目的:
- 将两个异步计算合并为一个(这两个异步计算之间相互独立,同时第二个又依赖于第一个的结果)
- 等待 Future 集合中的所有任务都完成。
- 仅等待 Future 集合中最快结束的任务完成,并返回它的结果。
二、CompletableFuture
1. 什么是CompletableFuture
使用Future获得异步执行结果时,要么调用阻塞方法get(),要么轮询看isDone()是否为true,这两种方法都不是很好,因为主线程也会被迫等待。
Java 在 1.8 版本提供了 CompletableFuture 来支持异步编程。
CompletableFuture类实现了CompletionStage和Future接口,因此你可以像Future那样使用它。它针对Future做了改进,可以传入回调对象,当异步任务完成或者发生异常时,自动调用回调对象的回调方法。
2. 如何使用CompletableFuture
使用CompletableFuture
参考URL: https://www.liaoxuefeng.com/wiki/1252599548343744/1306581182447650
3. demo举例
3.1 以获取股票价格为例,看看如何使用CompletableFuture
public class CompletableFutureDemo {public static void main(String[] args) throws Exception {// 创建异步执行任务:CompletableFuture<Double> cf = CompletableFuture.supplyAsync(CompletableFutureDemo::fetchPrice);// 如果执行成功:cf.thenAccept((result) -> {System.out.println("price: " + result);});// 如果执行异常:cf.exceptionally((e) -> {e.printStackTrace();return null;});// 主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:Thread.sleep(200);}static Double fetchPrice() {try {Thread.sleep(100);} catch (InterruptedException e) {}if (Math.random() < 0.3) {throw new RuntimeException("fetch price failed!");}return 5 + Math.random() * 20;}}
创建一个CompletableFuture是通过CompletableFuture.supplyAsync()实现的,它需要一个实现了Supplier接口的对象:
public interface Supplier<T> {T get();
}
这里我们用lambda语法简化了一下,直接传入Main::fetchPrice,因为Main.fetchPrice()静态方法的签名符合Supplier接口的定义(除了方法名外)。
紧接着,CompletableFuture已经被提交给默认的线程池执行了,我们需要定义的是CompletableFuture完成时和异常时需要回调的实例。
完成时,CompletableFuture会调用Consumer对象:
可见CompletableFuture的优点是:
- 异步任务结束时,会自动回调某个对象的方法;
- 异步任务出错时,会自动回调某个对象的方法;
- 主线程设置好回调后,不再关心异步任务的执行。
3.2 CompletableFuture描述实现串行关系、AND 汇聚关系、 OR汇聚关系
参考URL: https://time.geekbang.org/column/article/91569
站在分工的角度类比一下工作流。任务是有时序关系的,比如有串行关系、并行关系、汇聚关系等。
CompletableFuture 类还实现了 CompletionStage 接口,CompletionStage 接口可以清晰地描述任务之间的这种时序关系。
描述串行关系
CompletionStage 接口里面描述串行关系,主要是 thenApply、thenAccept、thenRun 和 thenCompose 这四个系列的接口。描述 AND 汇聚关系
CompletionStage 接口里面描述 AND 汇聚关系,主要是 thenCombine、thenAcceptBoth 和 runAfterBoth 系列的接口,这些接口的区别也是源自 fn、consumer、action 这三个核心参数不同。描述 OR 汇聚关系
CompletionStage 接口里面描述 OR 汇聚关系,主要是 applyToEither、acceptEither 和 runAfterEither 系列的接口,这些接口的区别也是源自 fn、consumer、action 这三个核心参数不同。异常处理
非异步编程里面,我们可以使用 try{}catch{}来捕获并处理异常,那在异步编程里面,异常该如何处理呢?
CompletionStage 接口给我们提供的方案非常简单,比 try{}catch{}还要简单,下面是相关的方法,使用这些方法进行异常处理和串行操作是一样的,都支持链式编程方式。CompletionStage exceptionally(fn); CompletionStage<R> whenComplete(consumer); CompletionStage<R> whenCompleteAsync(consumer); CompletionStage<R> handle(fn); CompletionStage<R> handleAsync(fn);
exceptionally() 的使用非常类似于 try{}catch{}中的 catch{}。
三、参考
Java并发编程系列一:Future和CompletableFuture解析与使用
参考URL: https://www.cnblogs.com/happyliu/archive/2018/08/12/9462703.html
JAVA并发-Future/CompletableFuture相关推荐
- Java并发Future
一.简介 ThreadPoolExecutor线程池获取任务执行结果,用到Future可以实现. 二.获取执行结果 Java通过ThreadPoolExecutor 提供3个submit()方法和1个 ...
- [Java并发-14] Future: 优雅的使用多线程
上一篇,我们详细介绍了如何创建正确的线程池,那创建完线程池,我们该如何使用呢?在上一篇文章中,我们仅仅介绍了 ThreadPoolExecutor 的 void execute(Runnable co ...
- Java并发编程Future超详细教程
非原创,自己保存学习用,传播请参考原文. 原文链接:Java并发编程Future超详细教程 前言 创建线程有几种方式?这个问题的答案应该是可以脱口而出的吧 继承 Thread 类实现 Runnable ...
- java并发编程Future类详解
作用和举例 future类的作用就是为了调用其他线程完成好后的结果,再返回到当前线程中,如上图举例: 小王自己是主线程,叫外卖等于使用future类,叫好外卖后小王就接着干自己的事去了,当外卖到了的时 ...
- 15、Java并发编程:Callable、Future和FutureTask
Java并发编程:Callable.Future和FutureTask 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一 ...
- Java并发编程 - Executor,Executors,ExecutorService, CompletionServie,Future,Callable
一.Exectuor框架简介 Java从1.5版本开始,为简化多线程并发编程,引入全新的并发编程包:java.util.concurrent及其并发编程框架(Executor框架). Executor ...
- futuretask java 并发请求_Java并发编程:Callable、Future和FutureTask
Java并发编程:Callable.Future和FutureTask 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一 ...
- futuretask java 并发请求_Java并发机制(9)--Callable、Future、FutureTask的使用
Java并发编程:Callable.Future.FutureTask的使用 继承关系: 1.接口Callable与Runnable 1.1.Runnable接口中只有一个void run()方法,其 ...
- 【Java并发】Runnable、Callable、Future、FutureTask
创建线程的两种方式 直接继承 Thread 实现 Runnable 接口 这两种方式都有一个缺点:在执行完成任务之后,无法直接获取到最后的执行结果.如果需要获取执行结果,就必须通过共享变量或线程通信的 ...
最新文章
- python 流写入文件_python文件流操作
- Java核心技术第五章——2.Object类
- POJ 2389 Bull Math(大数乘大数)
- SAP MM MM17里不能修改物料主数据'Purchasing Value Key'字段值?
- “用手机就能访问卫星” 软件定义升级卫星智能
- 几个让我印象深刻的面试题(二)
- Jupyter Nodebook添加代码提示(Vscode配置Jupyter Notebook运行.ipynb文件)
- CRNN:端到端不定长文字识别算法
- SQL概述及在网络安全中的应用
- package.json mysql_package.json入门
- .NET Core 时代已经到了,你准备好了吗
- 你会选择深圳还是佛山?
- 关注健康,从现在开始(视力篇)
- 七年也扶不起的苹果 Siri
- 吴恩达老师的机器学习和深度学习课程笔记打印版(全)
- java数组整组处理_java – 使它漂亮:同时处理数组
- matlab神经网络工具箱
- origin数据平滑_origin怎样平滑曲线 看完你就会了
- 我的Unity工具类---对象池
- c语言如何画函数图形,c语言绘制函数曲线
热门文章
- dst发育筛查有意义吗_Dst发育筛查是什么?
- matlab实现模糊控制器并仿真,用Matlab实现空调温度模糊控制器的设计与仿真.pdf...
- 如何将彩色证件照调成黑白
- VBA将ppt保存为html,ppt2013怎样保存为网页的方法
- conda:安装python
- ssh突然无法登录,报错failed to start openssh daemon
- Linux工作站usb口无反应,usb接口没反应怎么办 usb接口没反应解决方法
- js UUID 生成
- 广义Pareto分布---极值理论的学习3
- [计算机组成原理]2-6、算数移位、逻辑移位、循环移位