CompletableFuture
上手使用
异步编排: 能异步执行其他线程
第二次调用时:
then:
接下来执行什么
when:
当某个事件发生以后回调我们的逻辑
async:
异步方式completableFuture实现了接口 CompletionStage,此接口表示任务的时序关系,串行任务、并行任务、汇聚任务等。f3 = f1.thenCombine(f2, ()->{}) 描述的就是一种汇聚关系。f1和f2结束后
方法 | 说明 | 补充 |
---|---|---|
runAsync | 启动一个的异步线程 | 可以自己传入 executor 线程池。默认使用内部线程池 |
supplyAsync | 启动一个的异步线程 | 他与runAsync最大区别在与 supply是有返回值的 runAsync(Runnable runnable) supplyAsync(Supplier supplier) |
allOf/get | 阻塞等待异步线程完成,等待一个/全部 | |
thenApply | 接收上一个任务的返回值,自身有返回值 | thenApply 系列函数里参数 fn 的类型是接口 Function,这个接口里与 CompletionStage 相关的方法是 R apply(T t),这个方法既能接收参数也支持返回值,所以 thenApply 系列方法返回的是CompletionStage。 |
thenAccept | 接收上一个任务的返回值,自身没返回值 | 而 thenAccept 系列方法里参数 consumer 的类型是接口Consumer,这个接口里与 CompletionStage 相关的方法是 void accept(T t),这个方法虽然支持参数,但却不支持回值,所以 thenAccept 系列方法返回的是CompletionStage。 |
thenRun | 不接收参数,不能有返回值 | thenRun 系列方法里 action 的参数是 Runnable,所以 action 既不能接收参数也不支持返回值,所以 thenRun 系列方法返回的也是CompletionStage。 |
exceptionally | 异步线程出现问题的,异常处理 | 只能捕获异常不能回滚事务 |
handle | 捕获异常后的处理 | |
thenCombine | 合并2个任务 |
// 合并 and关系
CompletionStage<R> thenCombine(other, fn);
CompletionStage<R> thenCombineAsync(other, fn);
CompletionStage<Void> thenAcceptBoth(other, consumer);
CompletionStage<Void> thenAcceptBothAsync(other, consumer);
CompletionStage<Void> runAfterBoth(other, action);
CompletionStage<Void> runAfterBothAsync(other, action);
// 合并 or 的关系
CompletionStage applyToEither(other, fn);
CompletionStage applyToEitherAsync(other, fn);
CompletionStage acceptEither(other, consumer);
CompletionStage acceptEitherAsync(other, consumer);
CompletionStage runAfterEither(other, action);
CompletionStage runAfterEitherAsync(other, action);
CompletableFuture<Boolean> F1 = CompletableFuture.supplyAsync(() -> {
int i = 1/0;
return false;
}, threadPool).handle( (result,e)->{
if(e!=null){
System.out.println(e.getMessage());
throw new RuntimeException("发生异常");
}
return result;
});
CompletableFuture<BigDecimal> future = CompletableFuture.supplyAsync(() -> {
System.out.println("开启一个无返回值 异步 ");
int i = 1/0;
return new BigDecimal(10);
}).exceptionally(e -> new BigDecimal(20));//异步调用出现异常 执行内容
// get 阻塞当前线程等待 异步结果
System.out.println( future.get().toString());
开启一个无返回值 异步
20
System.out.println( "当前线程:" + Thread.currentThread().getName());
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
System.out.println(" 开启了异步任务 " + Thread.currentThread().getName());
return "异步任务 一";
});
CompletableFuture<String> futureWhen = future.thenApplyAsync(str -> {
System.out.println(" 获取到之前线程的return:" + str + Thread.currentThread().getName());
return str + "/" + "异步任务二继续执行";
});
System.out.println( futureWhen.get() + "//" + Thread.currentThread().getName() );
当前线程:main
开启了异步任务 ForkJoinPool.commonPool-worker-1
获取到之前线程的return:异步任务 一ForkJoinPool.commonPool-worker-1
异步任务 一/异步任务二继续执行//main
CompletableFuture<String> f0 =
CompletableFuture.supplyAsync(
() -> "Hello World")
.thenApply(s -> s + " QQ")
.thenApply(String::toUpperCase);
System.out.println(f0.join());
//输出结果
HELLO WORLD QQ
Future
线程池配合
Future
实现,但是阻塞主请求线程, 高并发时依然会造成线程数过多、CPU上下文切换。通过Future
可以并发发出N个请求,让后等待最慢一个返回,总响应时间为最慢的一个请求返回的用时。例如:Thread A 20ms、Thread B 15ms、Thread 30ms 最终响应在30ms返回
异步Callabck
通过回调机制实现,首先发出网络请求,当网络返回时回调相关方法,如
HttpAsyncClien
使用基于NIO的一步 I/O模型实现,它实现了Reactor
模式,摒弃了阻塞 I/O模型one thread per connection
,采用线程池分发事件通知,从而有效支撑大量并发链接。这种机制并不能提升性能,而是为了支撑大量并发链接或者提升吞吐量。
CompletableFuture
CompletableFuture
提供了心的异步编程思路,可以对多个异步处理进行编排,实现更加复杂的异步处理。其内部使用ForkJoinPool
实现异步处理。使用CompletatbleFuture
可以吧回调方式实现转变为同步调用实现。
列子
//任务1:洗水壶->烧开水
CompletableFuture<Void> f1 =
CompletableFuture.runAsync(()->{
System.out.println("T1:洗水壶...");
sleep(1, TimeUnit.SECONDS);
System.out.println("T1:烧开水...");
sleep(15, TimeUnit.SECONDS);
});
//任务2:洗茶壶->洗茶杯->拿茶叶
CompletableFuture<String> f2 =
CompletableFuture.supplyAsync(()->{
System.out.println("T2:洗茶壶...");
sleep(1, TimeUnit.SECONDS);
System.out.println("T2:洗茶杯...");
sleep(2, TimeUnit.SECONDS);
System.out.println("T2:拿茶叶...");
sleep(1, TimeUnit.SECONDS);
return "龙井";
});
//任务3:任务1和任务2完成后执行:泡茶
CompletableFuture<String> f3 =
f1.thenCombine(f2, (__, tf)->{
System.out.println("T1:拿到茶叶:" + tf);
System.out.println("T1:泡茶...");
return "上茶:" + tf;
});
//等待任务3执行结果
System.out.println(f3.join());
void sleep(int t, TimeUnit u) {
try {
u.sleep(t);
}catch(InterruptedException e){}
}
// 一次执行结果:
T1:洗水壶...
T2:洗茶壶...
T1:烧开水...
T2:洗茶杯...
T2:拿茶叶...
T1:拿到茶叶:龙井
T1:泡茶...
上茶:龙井
异常处理
接口中 fn、consumer、action 它们的核心方法都不允许抛出可检查异常。运行时可以抛出异常,非异步的编程中使用try{}catch{}来捕获并处理异常。具体需要关注CompletionStage类中的方法
CompletionStage exceptionally(fn);
CompletionStage<R> whenComplete(consumer);
CompletionStage<R> whenCompleteAsync(consumer);
CompletionStage<R> handle(fn);
CompletionStage<R> handleAsync(fn);
列子
下面的示例代码展示了如何使用 exceptionally() 方法来处理异常,exceptionally() 的使用非常类似于 try{}catch{}中的 catch{},但是由于支持链式编程方式,所以相对更简单。既然有 try{}catch{},那就一定还有 try{}finally{},whenComplete() 和 handle() 系列方法就类似于 try{}finally{}中的 finally{},无论是否发生异常都会执行 whenComplete() 中的回调函数 consumer 和 handle() 中的回调函数 fn。whenComplete() 和 handle() 的区别在于 whenComplete() 不支持返回结果,而 handle() 是支持返回结果的。
CompletableFuture<Integer>
f0 = CompletableFuture
.supplyAsync(()->(7/0))
.thenApply(r->r*10)
.exceptionally(e->0);
System.out.println(f0.join());
是固定线程池
关键点
CompletableFuture.runAsync( () -> System.out.println(Thread.currentThread().getName())
,threadPoolExecutor);
//异步线程池
public static ThreadPoolExecutor newPool() {
return new ThreadPoolExecutor(
2, 10, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10),
new ThreadFactory() {
int i = 0;
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("异步任务测试 -- "+ i++);
return thread ;
}
}
, new ThreadPoolExecutor.CallerRunsPolicy()
);
}
//启动100次线程
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = newPool();
for (int i = 0; i < 100; i++) {
CompletableFuture.runAsync( () -> System.out.println(Thread.currentThread().getName())
,threadPoolExecutor);
};
System.out.println("--------------");
}
循环调用
public class CompletableFutureFor {
//异步线程池
public static ThreadPoolExecutor newPool() {
return new ThreadPoolExecutor(
2, 10, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10),
new ThreadFactory() {
int i = 0;
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("异步任务测试 -- "+ i++);
return thread ;
}
}
, new ThreadPoolExecutor.CallerRunsPolicy()
);
}
//启动100次线程
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = newPool();
CopyOnWriteArrayList<Integer> carr = new CopyOnWriteArrayList<>();
AtomicInteger count = new AtomicInteger();
List<CompletableFuture<Void>> futures = IntStream.rangeClosed(1, 100)
.mapToObj(n ->
CompletableFuture.runAsync(() -> {
count.incrementAndGet();
if(n == 10){ // 异常模拟
int err = n/0;
throw new RuntimeException("单次出现异常" + n);
}
carr.add(n);
}, threadPoolExecutor)).collect(toList());
try {
futures.forEach(CompletableFuture::join); // 发现异常立刻抛出
} catch (Exception e) {
System.out.println("结束");
}
// 等待所有的线程结束
CompletableFuture<Void> headerFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[]{}));
try{
headerFuture.join();
}catch (Exception e){
System.out.println("执行数量问题" + count.get());
throw new RuntimeException("其中一个线程出现问题终止任务");
}
System.out.println("执行数量" + count.get());
List<Integer> arr = carr.stream().sorted().collect(toList());
System.out.println(arr.toString());
}
}