提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
前言
CompletableFuture在JUC中扮演着非常重要的角色
一、Future是什么?
在Java中作为异步任务的顶级接口,定义了一些操作异步任务以及判断异步任务的执行情况是完成还是已取消,提供了一种异步任务计算的功能;主要协助主线程来完成一些复杂的业务
二、FutureTask实现类
FutureTask类实现了RunnableFuture而RunnableFuture继承了Runnable和Future,如下图:
在FutureTask类中存在Callable接口,所以对于FutureTask能够实现异步任务并且具有返回值的这样一个类
对于FutureTask有什么优缺点呢?
优点:
- 简单易用:FutureTask是Java标准库提供的基本异步任务类,使用起来相对简单。只需实现Callable接口或使用匿名类即可,无需复杂的配置或设置。
- 兼容性广泛:FutureTask是较早引入的类,因此在各种Java版本和框架中都能使用,具有良好的兼容性。
- 灵活的任务组合:当主线程需要执行一个很耗时的子任务时,可以通过FutureTask把这个任务放到异步线程去执行,主线程可以继续执行其他任务或者先行结束。等过一会儿再去获取子任务执行的结果,这大大提高了程序的执行效率。
- 支持异常处理:与Callable相比,FutureTask的get方法会抛出ExecutionException,可以捕获底层异常。
缺点:
- 缺乏方法链式调用:在处理多个异步任务的组合和转换时,FutureTask提供的get()方法虽然可以获取结果,但相比于其他类(如CompletableFuture),其灵活性和便利性稍显不足。
- 异常处理不够直观:虽然FutureTask支持异常处理,但需要通过手动捕获异常或添加额外的错误处理代码,这在某些情况下可能会增加代码的复杂性。
- 可能导致阻塞:当使用FutureTask的get方法获取结果时,如果任务尚未完成,调用线程将阻塞,直到任务完成。这可能会降低程序的响应性,特别是在需要高并发或低延迟的场景中。
1.1FutureTask的get方法可能造成阻塞
因为其具有返回值,调用出需要拿到返回值才能进行下一步操作,在实际开发过程中是不建议使用get方法的,如下图代码
输出结果:
由执行结果得出结论,get方法会阻塞调用线程
设置Timeout:
Integer i = task.get(2, TimeUnit.SECONDS);//阻塞
超时处理:报出了异常
1.2怎样解决阻塞?
可以通过while(true)的方式来实现,来判断执行情况,添加如下代码:
while(true){
if(task.isDone()){
Integer i = task.get();
System.out.println("数据处理完毕");
System.out.println(i);
break;
}else{
System.out.println("数据处理中");
Thread.sleep(2000);
}
}
执行结果:
这样虽然不像前面一样阻塞的等待,但是使用while(true)的方式,CPU的空轮询,造成资源的浪费
二、CompletableFuture的使用
2.1基本介绍
CompletableFuture是Java 8中引入的一种实现异步编程模型的方式,它是Future接口的扩展和增强,提供了更强大、更灵活的功能。以下是关于CompletableFuture的详细介绍:
- 异步执行:CompletableFuture支持任务的异步执行,可以使用如supplyAsync()等方法来创建一个异步执行的任务,并返回一个CompletableFuture对象。这种异步执行方式不会阻塞主线程,提高了应用程序的响应性和性能。
- 链式操作:CompletableFuture提供了丰富的方法来实现链式操作,可以方便地对任务进行组合、转换和结果处理。这使得代码更加易读和维护,同时提高了处理多个异步任务时的灵活性和便利性。
- 异常处理:CompletableFuture提供了灵活的异常处理方法,可以处理任务执行过程中可能发生的异常,并实现回退机制。这使得在编写异步代码时,可以更加便捷地处理可能出现的异常情况。
- 多任务组合:CompletableFuture支持多个任务的并发执行和结果组合,可以轻松实现多任务并发处理的场景,提高应用程序的效率和并发性。
相比传统的Future接口,CompletableFuture更加灵活和强大。它解决了Future在某些场景下使用不便的问题,如复杂的逻辑处理、难以维护的代码结构等。同时,CompletableFuture的链式操作特性也使得代码更加简洁和直观。
2.2方法介绍
四大静态方法:
无返回值,可以指定线程池,也可以不指定
public static CompletableFuture<Void> runAsync(Runnable runnable) {
return asyncRunStage(ASYNC_POOL, runnable);
}
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor) {
return asyncRunStage(screenExecutor(executor), runnable);
}
有返回值,可以指定线程池,也可以不指定
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
return asyncSupplyStage(ASYNC_POOL, supplier);
}
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor{
return asyncSupplyStage(screenExecutor(executor), supplier);
}
2.3方法使用
CompletableFuture<Void> future =CompletableFuture.runAsync(()->{
System.out.println("无返回,不指定线程池");
System.out.println("没有指定的默认线程池"+Thread.currentThread().getName());
});
System.out.println(future.get());//null
ExecutorService pool = Executors.newFixedThreadPool(3);
CompletableFuture<Void> future1 =CompletableFuture.runAsync(()->{
System.out.println("无返回,指定线程池");
System.out.println("指定线程"+Thread.currentThread().getName());
},pool);
System.out.println(future.get());//null
执行结果:
CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("有返回值,无线程池" + Thread.currentThread().getName());
return "返回值1";
});
System.out.println(stringCompletableFuture.get());
CompletableFuture<String> stringCompletableFuture1 = CompletableFuture.supplyAsync(() -> {
System.out.println("有返回值,有线程池" + Thread.currentThread().getName());
return "返回值2";
},pool);
System.out.println(stringCompletableFuture1.get());
执行结果:
总结
这次主要是一些API的基本使用,以后会继续更新!