1.JAVA多线程(二十四)Java多线程之CompletableFuture类
1.1 什么是Future
Future是Java 5添加的类,用来描述一个异步计算的结果。你可以使用isDone方法检查计算是否完成,或者使用get阻塞住调用线程,直到计算完成返回结果,你也可以使用cancel方法停止任务的执行。
package com.yuanxw.chapter24;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
public class FutureExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 构造一个线程池
ExecutorService executorService = Executors.newCachedThreadPool();
// 提交线程任务
Future<Integer> future = executorService.submit(() -> {
try {
System.out.println(String.format("当前【%s】线程工作-开始...", Thread.currentThread().getName()));
TimeUnit.SECONDS.sleep(10);
System.out.println(String.format("当前【%s】线程工作-结束...", Thread.currentThread().getName()));
return ThreadLocalRandom.current().nextInt(100);
} catch (InterruptedException e) {
e.printStackTrace();
return -1;
}
});
System.out.println(String.format("【%s】在等待线程执行结果...", Thread.currentThread().getName()));
System.out.println("是否执行结束:"+future.isDone());
System.out.println(future.get());
System.out.println("是否执行结束:"+future.isDone());
}
}
执行结果:
当前【pool-1-thread-1】线程工作-开始...
【main】在等待线程执行结果...
是否执行结束:false
当前【pool-1-thread-1】线程工作-结束...
76
是否执行结束:true
1.2 Future的局限性
虽然Future以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的初衷相违背,轮询的方式又会耗费无谓的CPU资源,而且也不能及时地得到计算结果。CompletableFuture的出现解决了Future模式的缺点。
- 不能手动完成:
- 当你写了一个函数,用于通过一个远程API获取一个电子商务产品最新价格。因为这个 API 太耗时,你把它允许在一个独立的线程中,并且从你的函数中返回一个 Future。现在假设这个API服务宕机了,这时你想通过该产品的最新缓存价格手工完成这个Future 。你会发现无法这样做。
- Future的结果在非阻塞的情况下,不能执行更进一步的操作:
- Future 不会通知你它已经完成了,它提供了一个阻塞的 get() 方法通知你结果。你无法给 Future 植入一个回调函数,当 Future 结果可用的时候,用该回调函数自动的调用 Future 的结果。
- 多个Future不能串联在一起组成链式调用:
- 有时候你需要执行一个长时间运行的计算任务,并且当计算任务完成的时候,你需要把它的计算结果发送给另外一个长时间运行的计算任务等等。你会发现你无法使用 Future 创建这样的一个工作流。
不能组合多个 Future 的结果
假设你有10个不同的Future,你想并行的运行,然后在它们运行未完成后运行一些函数。你会发现你也无法使用 Future 这样做。
- 有时候你需要执行一个长时间运行的计算任务,并且当计算任务完成的时候,你需要把它的计算结果发送给另外一个长时间运行的计算任务等等。你会发现你无法使用 Future 创建这样的一个工作流。
- 没有异常处理:
- Future API 没有任务的异常处理结构居然有如此多的限制,幸好我们有CompletableFuture,你可以使用 CompletableFuture 达到以上所有目的。
1.3 什么是CompletableFuture
在Java中CompletableFuture用于异步编程,异步编程是编写非阻塞的代码,运行的任务在一个单独的线程,与主线程隔离,并且会通知主线程它的进度,成功或者失败。在这种方式中,主线程不会被阻塞,不需要一直等到子线程完成。主线程可以并行的执行其他任务。使用这种并行方式,可以极大的提高程序的性能。
CompletableFuture 实现了 Future 和 CompletionStage接口,并且提供了许多关于创建,链式调用和组合多个 Future 的便利方法集,而且有广泛的异常处理支持。
CompletableFuture和Java8的Stream搭配使用,使用对于一些并行访问的耗时操作有很大的操作。
CompletableFuture继承结构关系图:
CompletableFuture类实现了CompletionStage和Future接口,所以你还是可以像以前一样通过阻塞或者轮询的方式获得结果,尽管这种方式不推荐使用。
1.4 CompletableFuture方法分类
1.4.1. 创建CompletableFuture对象
以Async结尾并且没有指定Executor的方法会使用ForkJoinPool.commonPool()作为它的线程池执行异步代码。runAsync方法也好理解,它以Runnable函数式接口类型为参数,所以CompletableFuture的计算结果为空。supplyAsync方法以Supplier<U>函数式接口类型为参数,CompletableFuture的计算结果类型为U。
CompletableFuture的静态工厂方法:
方法名 | 描述 |
---|---|
runAsync(Runnable runnable) | 使用ForkJoinPool.commonPool()作为它的线程池执行异步代码。 |
runAsync(Runnable runnable, Executor executor) | 使用指定的thread pool执行异步代码。 |
supplyAsync(Supplier supplier) | 使用ForkJoinPool.commonPool()作为它的线程池执行异步代码,异步操作有返回值。 |
supplyAsync(Supplier supplier, Executor executor) | 使用指定的thread pool执行异步代码,异步操作有返回值。 |
- CompletableFuture.completedFuture是一个静态辅助方法,用来返回一个已经计算好的CompletableFuture。
/**
* Returns a new CompletableFuture that is already completed with
* the given value.
*
* @param value the value
* @param <U> the type of the value
* @return the completed CompletableFuture
*/
public static <U> CompletableFuture<U> completedFuture(U value) {
return new CompletableFuture<U>((value == null) ? NIL : value);
}
- 而以下四个静态方法用来为一段异步执行的代码创建CompletableFuture对象:
/**
* Returns a new CompletableFuture that is asynchronously completed
* by a task running in the {@link ForkJoinPool#commonPool()} with
* the value obtained by calling the given Supplier.
*
* @param supplier a function returning the value to be used
* to complete the returned CompletableFuture
* @param <U> the function's return type
* @return the new CompletableFuture
*/
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
return asyncSupplyStage(asyncPool, supplier);
}
/**
* Returns a new CompletableFuture that is asynchronously completed
* by a task running in the given executor with the value obtained
* by calling the given Supplier.
*
* @param supplier a function returning the value to be used
* to complete the returned CompletableFuture
* @param executor the executor to use for asynchronous execution
* @param <U> the function's return type
* @return the new CompletableFuture
*/
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor) {
return asyncSupplyStage(screenExecutor(executor), supplier);
}
/**
* Returns a new CompletableFuture that is asynchronously completed
* by a task running in the {@link ForkJoinPool#commonPool()} after
* it runs the given action.
*
* @param runnable the action to run before completing the
* returned CompletableFuture
* @return the new CompletableFuture
*/
public static CompletableFuture<Void> runAsync(Runnable runnable) {
return asyncRunStage(asyncPool, runnable);
}
/**
* Returns a new CompletableFuture that is asynchronously completed
* by a task running in the given executor after it runs the given
* action.
*
* @param runnable the action to run before completing the
* returned CompletableFuture
* @param executor the executor to use for asynchronous execution
* @return the new CompletableFuture
*/
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor) {
return asyncRunStage(screenExecutor(executor), runnable);
}
1.4.2. 主动完成计算
方法名 | 描述 |
---|---|
get() | 方法同步等待结果。 |
get(timeout,unit) | get(timeout,unit)签名方法,给定时间,然后返回其结果,如果超时,抛出异常 |
join() | 完成后返回结果值,如果完成异常,则返回(未检查)异常。 |
getNow() | 如果已完成,则返回结果值(或抛出任何遇到的异常),否则返回给定的值IfAbsent。 |
package com.yuanxw.chapter24;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class CompletableFutureExample1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
runAsync();
// get() 方法同步等待结果。
get();
// get(timeout,unit)签名方法,给定时间,然后返回其结果,如果超时,抛出异常
get(3,TimeUnit.SECONDS);
// join() 完成后返回结果值,如果完成异常,则返回(未检查)异常。
join();
// getNow() 如果已完成,则返回结果值(或抛出任何遇到的异常),否则返回给定的值IfAbsent。
getNow();
}
/**
* runAsync()异步执行runAsync返回的CompletableFuture是没有返回值的。
* supplyAsync()异步执行runAsync返回的CompletableFuture是有返回值的。
执行结果:
=====【main】执行runAsync()签名方法-开始=====
Hello world!!!
=====【main】执行runAsync()签名方法-结束=====
* @throws ExecutionException
* @throws InterruptedException
*/
private static void runAsync() throws ExecutionException, InterruptedException {
System.out.println(String.format("=====【%s】执行runAsync()签名方法-开始=====", Thread.currentThread().getName()));
CompletableFuture.runAsync(