CompletableFuture
参考:https://blog.csdn.net/u012129558/article/details/78962759
https://www.jianshu.com/p/bdc6bd50f7d2
Future模式的缺点
- Future虽然可以实现获取异步执行结果的需求,但是它没有提供通知的机制,我们无法得知Future什么时候完成。
- 要么使用阻塞,在future.get()的地方等待future返回的结果,这时又变成同步操作。要么使用isDone()轮询地判断Future是否完成,这样会耗费CPU的资源。
CompletableFuture介绍
Java 8新增的CompletableFuture类正是吸收了所有Google Guava中ListenableFuture和SettableFuture的特征,还提供了其它强大的功能,让Java拥有了完整的非阻塞
编程模型:Future
、Promise
和 Callback
(在Java8之前,只有无Callback 的Future)。
CompletableFuture弥补了Future模式的缺点。在异步的任务完成后,需要用其结果继续操作时,无需等待。可以直接通过thenAccept、thenApply、thenCompose等方式将前面异步处理的结果交给另外一个异步事件处理线程来处理。
1.创建对象
CompletableFuture
有两类最基本的创建对象的静态方法:
CompletableFuture的静态工厂方法
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor);
public static CompletableFuture<Void> runAsync(Runnable runnable);
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);
runAsync
和 supplyAsync
方法的区别是runAsync
返回的CompletableFuture
是没有返回值的。
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName()+"hello!");
});
try {
Void aVoid = future.get();
System.out.println(aVoid); //null
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
ForkJoinPool.commonPool-worker-1hello!
null
ExecutorService executor = Executors.newFixedThreadPool(3);
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "world!",executor);
try {
System.out.println(future.get()); //world!
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
2.获取对象内容
CompletableFuture有几个相关方法,包括:
public T get() throws InterruptedException, ExecutionException;
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
public T join();
public T getNow(T valueIfAbsent);
get 方法会使当前线程阻塞
,并且等待直到 future 完成,并且将返回 future 的值- 带参数的 get 方法需要传入对应的时间长度,一旦超出这个时间,会抛出TimeoutException
join 方法与 get 并无太大差别
,但 join 方法不能被打断- getNow 方法不会阻塞线程,而是立即返回值,如果该 future 当前没有完成,则会立刻返回该方法的传入参数.
- get 和 join 方法会在正常的情况下返回值,在遇到异常的情况下将异常抛出
说明:通常情况下,为了使所有的多线程都执行完后,再进行下一步处理(如return
等),可以使用join
。虽然join
可以获取结果,但这里是为了使用其阻塞的作用。
1.不阻塞将不会等待结果
ExecutorService executor = Executors.newFixedThreadPool(3);
List<String> list = Lists.newArrayList("hi","didi","hello");
List<String> nList = Lists.newArrayList();
list.forEach(s->CompletableFuture.runAsync(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
nList.add(s.toUpperCase());
}, executor));
System.out.println(nList);//[]
上述由于异步处理时需要花费1s时间,故nList为空。可以等待1s以后,再查看nList的内容:
ExecutorService executor = Executors.newFixedThreadPool(3);
List<String> list = Lists.newArrayList("hi","didi","hello");
List<String> nList = Lists.newArrayList();
list.forEach(s->CompletableFuture.runAsync(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
nList.add(s.toUpperCase());
}, executor));
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(nList);//[HI, DIDI, HELLO]
上述等待一定时间后,nList可能已经有了全部结果,可能结果不是很全。通过等待时间不是好的做法,我们可以通过join来等待多线程运行结束。
2.join等待多线程运行结束
ExecutorService executor = Executors.newFixedThreadPool(3);
List<String> list = Lists.newArrayList("hi","didi","hello");
List<String> nList = Lists.newArrayList();
list.forEach(s->CompletableFuture.runAsync(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
nList.add(s.toUpperCase());
}, executor).join());
System.out.println(nList);//[HI, DIDI, HELLO]
join将等待所有线程执行完后,执行下一步。
thread1 thread2 thread3
\ | /
join(都有结果了往下走)