文章目录
一.CompletableFuture简介
为了满足Java在处理异步任务的一些不足,比如说对返回值获取不友好等,在1.8版本推出了CompletableFuture,它实现了Future和CompletionStage接口,拥有Futrue的全部功能,并拥有更加强大的异步回调,执行级联任务等等优点.
二.CompletableFuture简单入门使用
1.创建对象
对于一般Java对象来说,最常见得就是直接new一个对象,但是CompletableFuture还是有点特别得,我们可以看到文档中已经说明,通过构造器会创建一个不完整得CompletableFuture对象.所以是不推荐使用空参构造方法来创建一个CompletableFuture对象得.
那要怎样获得这个对象呢?我们可以通过两组,四个方法进行获取.
这里需要注意Executor(线程池)得参数说明:
并且这里得ForkJoinPool.commonPool()创建得线程是守护线程(不理解守护线程的,可以看一下这个用户线程和守护线程的区别),这一点在第三节中会证明.
2.代码演示-无返回值类型
方法:CompletableFuture.runAsync()
使用自带得线程池(注意自带得是守护线程)
//当我们传入任务返回对象得时候,线程任务已经开始执行了
//使用自带得线程池(注意自带得是守护线程)
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName());
});
//completableFuture 实现了Future<T>, CompletionStage<T> 接口,自然也会有Future得get()方法
System.out.println(completableFuture.get());
输出值:
使用自定义得线程池(默认用户线程)
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName());
},executorService);
//completableFuture 实现了Future<T>, CompletionStage<T> 接口,自然也会有Future得get()方法
System.out.println(completableFuture.get());
//使用线程池,记得进行关闭
executorService.shutdown();
输出值:
3.代码演示-有返回值类型
方法:CompletableFuture.supplyAsync
这里直接演示用自定义线程池得方法.
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName());
return "hello supplyAsync";
}, executorService);
//completableFuture 实现了Future<T>, CompletionStage<T> 接口,自然也会有Future得get()方法
System.out.println(completableFuture.get());
//使用线程池,记得进行关闭
executorService.shutdown();
输出结果:
三.通用演示,传入回调函数,减少阻塞
我们知道CompletableFuture拥有比Future更强大的功能,能够减少阻塞和轮询.
我们可以传入一个回调函数,当异步任务完成得时候,自动执行回调方法.
举例:使用自带线程池:
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("开始执行异步任务...这个任务预计耗时1s");
System.out.println("执行异步任务线程为:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello supplyAsync";
/**
* 传入回调函数,当顺利执行完毕后,会自动调用该函数
* 第一个参数result就是异步任务返回得结果
* 第二个参数e,就是这个过程中出现得异常对象
*/
}).whenComplete((result,e)->{
System.out.println("异步任务已经执行完毕...获取结果:"+result);
/**
* 当任务出现异常会走到这个分支,进行异常得处理
* 这里得参数还是异常
*/
}).exceptionally((e) -> {
e.printStackTrace();
System.out.println("出现异常:"+e.getCause());
//出现异常后返回得结果
return null;
});
System.out.println("主线程开始忙其他得事情...");
输出结果:
细心得同学可能已经发现有问题了,程序并没有输出返回结果,直接就结束了,这是为什么呢?
因为我们使用的是自带得线程池,还记得前面说自带得线程池创建出来的线程都是守护线程吗,这里就体现出来得,因为main线程先于异步任务线程执行完毕,此时JVM中已没有用户线程了,所以守护线程自动被关闭.所以使用该类最好使用自定义线程池
所以我们需要改进一下,使用我们自定义得线程池,改进代码如下:
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("开始执行异步任务...这个任务预计耗时1s");
System.out.println("执行异步任务线程为:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello supplyAsync";
/**
* 传入回调函数,当顺利执行完毕后,会自动调用该函数
* 第一个参数result就是异步任务返回得结果
* 第二个参数e,就是这个过程中出现得异常对象
*/
},executorService).whenComplete((result,e)->{
System.out.println("异步任务已经执行完毕...获取结果:"+result);
/**
* 当任务出现异常会走到这个分支,进行异常得处理
* 这里得参数还是异常
*/
}).exceptionally((e) -> {
e.printStackTrace();
System.out.println("出现异常:"+e.getCause());
//出现异常后返回得结果
return null;
});
System.out.println("主线程开始忙其他得事情...");
executorService.shutdown();
输出结果:
四.总结.
通过以上小练习我们能够知道CompletableFuture是具有如下这些优点的.