使用CompletableFuture遇到的坑

使用CompletableFuture遇到的坑

经典用法allof()

最近在做项目时,需要用到多线程,平时用的多的是callable用future来接收返回值,用线程池提交异步任务,或者countDownLatch来实现,具体会在别的章节里展开,突然间想起海哥有一次提到了CompletableFuture,就拿来体验一下,果然是有坑。

不废话上代码

static void test2() {

        List<Long> sqrLongs = new ArrayList<>();

        List<Long> longs = Arrays.asList(12L, 13L, 14L, 15L, 16L);

        List<CompletableFuture> completableFutures = Lists.newArrayList();
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            CompletableFuture completableFuture = CompletableFuture.runAsync(() -> {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Long aLong = longs.get(finalI);
                sqrLongs.add(aLong * aLong);
                log.info("异步线程=======>" + Thread.currentThread().getName() + "-" + System.currentTimeMillis());
            }, excecuter);

            completableFutures.add(completableFuture);
        }

        CompletableFuture<Void> allFuture = CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[completableFutures.size()]));
        try {
            log.info("get前=======>" + System.currentTimeMillis());
            allFuture.get();
            log.info("get后=======>" + System.currentTimeMillis());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

        log.info("主线程=======>" + System.currentTimeMillis());
        sqrLongs.stream().forEach(System.out::println);

    }
  1. 这里只是简单的算一个平方值,main方法运行一下,
public static void main(String[] args) throws InterruptedException {
        test2();
    }
  1. 输出结果:
    返回值
  2. CompletableFuture.allOf()会聚合出一个新的CompletableFuture,执行get()会阻塞当前主线程,异步执行后,结果出现了null值,视觉上是多线程没返回结果,猜测是线程超时?但是根本没有费时的操作,也没有设置超时时间。分析日志,pool-2-thread-1到5,全部是同一时刻执行完毕,联想到多线程,list是不安全的类。

修改代码为安全的类

 //注意⚠️这里使用线程安全的类
  List<Long> sqrLongs = new Vector<>();

运行结果:
多线程返回值
全部正常。

总结分析

join会阻塞主线程,直到所有线程都返回,这里使用的是自己创建的线程池,如果使用的是默认的线程池ForkJoinPool,会把主线程设置成守护线程,如果配合使用.whenComplete()不会阻塞主线程,主线程运行结束,异步自动退出,不会等待异步结束。所以,多线程的使用要求我们必须具备扎实的技术知识,在对一个Api底层原理不够清晰的情况下,还是容易出现问题的,正所谓:知己知彼,百战不殆。allof得使用线程安全的类,才能保证所有结果全部正常写入

  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值