CompletableFuture

本文介绍了CompletableFuture在Java中的使用,包括异步执行、回调机制、Future的对比、任务编排、异常处理和线程池应用实例。通过 CompletableFuture,可以高效地组织并行和串行任务,提升并发编程体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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());
    }

}

学习文章
循环调用
极客时间 推荐

底部

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值