Runnable、Callable、FutureTask、CompletableFuture
Runnable和Callable
都是代表一个任务,可以把这个任务交给线程或者线程池执行
- 都是函数式接口,可以用lambda表达式替代
- Runnable没有返回值,任务内不抛出异常
- Callable有返回值,任务内可以抛出异常,Callable需要用FutureTask包装才能交给线程执行
1 |
|
1 |
|
Callable和FutureTask
FutureTask实现了Runnable接口,Callable需要用FutureTask包装就可以交给线程执行
同时FutureTask是实现了Future接口,有get()方法等待异步任务结果,用了类似于Condiction条件队列的虚拟链表方式存储需要返回结果的任务
任务的状态
任务状态是不可逆的,初始任务在构造器里面就是NEW状态
和AQS一样,有一个state变量存储任务状态:
1 | private volatile int state; |
状态转换图
CompletableFuture
在之前的FutureTask中,如果想要获取到多线程执行的结果,有两种办法,一种是轮询FutureTask.isDone()
方法,当结果为true的时候获取执行结果,第二种则是调用FutureTask.get()
方法。但是无论那种方式都无法实现真正意义上的异步回调,因为任务执行需要时间,所以都会使得主线程被迫阻塞。
CompletableFuture对象实现异步任务
CompletableFuture也实现了Future接口,所以和FutureTask一样也可以用complete()完成异步任务,并且用get()阻塞等待。
CompletableFuture的静态方法实现异步任务
为了能够真正的实现异步,通过回调代替阻塞,可以用CompletableFuture的静态方法:
- runAsync和supplyAsync都支持用线程池进行任务调度,如果不为CompletableFuture指定线程池执行任务的情况下,默认是使用ForkJoinPool.commonPool()的线程,而ForkJoinPool.commonPool()的线程是作为main线程的守护线程进行的,如果main挂了,执行异步任务的线程也会随之终止结束,并不会继续执行异步任务
- runAsync没有返回值,而supplyAsync有返回值
1 | public static CompletableFuture<Void> runAsync(Runnable runnable); |
CompletableFuture成员方法实现异步任务
如果有Async后缀,分为以下3种情况:
- 上一个任务已经执行结束了,那么当前创建出的任务会交给上个任务的执行线程来执行
- 上一个任务还没有执行结束,那么则会另启一条线程来执行
- 如果创建任务时指定了执行线程池,则会使用指定线程池的线程来执行
一般有4种成员方法:因为CompletableFuture实现了CompletionStage接口,在每个任务执行完成后又回返回一个CompletableFuture对象,使用时我们可以接着基于该对象继续创建新的任务,可以链式调用
- thenApply()):此类方法可以基于上一个任务再创建一个新的有返回型任务。
- handle():与thenApply类作用相同,不同点在于thenApply类方法只能在上一个任务执行正常的情况下才能执行,当上一个任务执行抛出异常后则不会执行。而handle类在上个任务出现异常的情况下也可以接着执行。
- thenRun():此类方法可以基于上一个任务再创建一个新的无返回型任务。
- thenCompose():与thenApply类大致相同,不同点在于每次向下传递都是新的CompletableFuture对象,而thenApply向下传递的都是同一个CompletableFuture对象对象
异步回调和异常处理
- 主线程调用直接阻塞获取执行结果的
get、getNow
方法 - 可以为无返回值的异步任务写出执行结果的:
complete
开头的方法 - 任务正常执行成功的回调:
thenAccept
开头的方法 - 任务执行抛出异常的回调:
exceptionally
方法 - 任务执行结束的回调:
whenComplete
开头的方法
调用时序
串行
thenApply
、thenAccept
、thenRun
、handle
以及thenCompose
并行
汇聚
AND类型:全部异步任务都需要完成
- thenCombine系列:可以接收前面任务的结果进行汇聚计算,并且计算后可以返回值
- thenAcceptBoth系列:可以接收前面任务的结果进行汇聚计算,但计算后没有返回值
- runAfterBoth系列:不可以接收前面任务的结果且无返回,但可以在任务结束后进行汇聚计算
- CompletableFuture类的allOf系列:不可接收之前任务的结果,但可以汇聚多个任务,但是要配合回调处理方法一起使用
OR类型:只需要一个异步任务就行
- applyToEither系列:接收最先完成的任务结果进行处理,处理完成后可以返回值
- acceptEither系列:接收最先完成的任务结果进行处理,但是处理完成后不能返回
- runAfterEither系列:不能接收前面任务的返回值且无返回,单可以为最先完成的任务进行后继处理
- CompletableFuture类的anyOf系列:可以同时汇聚任意个任务,并接收最先执行完成的任务结果进行处理,处理完成后没有返回值,需要配合回调方法一起使用
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 白兰!