CompletableFuture高并发和线程池使用

在Java里,为提高单线程执行时间长的任务运行速率,常使用多线程。若需子线程执行完再执行主线程,有“多线程+CountDownLatch”和“线程池+CompletableFuture”两种方案。文章还介绍了CompletableFuture的特性,它为异步计算提供强大灵活的方式。
该文章已生成可运行项目,

概要

多线程+CountDownLatch

线程池+CompletableFuture

CompletableFuture介绍


概要

在Java中,有些任务单线程执行时间长,一般会使用多线程增加并发提高运行速率。但很多情况下,需要所有子线程执行完,才能往下执行主线程,一般我们会使用这些方案:

1、多线程+CountDownLatch

2、线程池+CompletableFuture

多线程+CountDownLatch

public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(10);
 
        for (int i=0; i<4; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " 运行");
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        latch.countDown();
                    }
                }
            }).start();
        }
 
        System.out.println("等待子线程运行结束");
        latch.await(10, TimeUnit.SECONDS);
        System.out.println("子线程运行结束");
}


 

缺点:经常会忘记写latch.countDown();,或者latch.countDown()未执行,导致线程阻塞

线程池+CompletableFuture

1、多个子线程全部执行完,才能继续往下,阻塞主线程

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(8, 10,
        10, TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(Integer.MAX_VALUE));
CompletableFuture<Void> task3 = CompletableFuture.runAsync(() -> {
    //TODO do something
}, threadPoolExecutor);

CompletableFuture<Void> task5 = CompletableFuture.runAsync(() -> {
    //TODO do something
}, threadPoolExecutor);
CompletableFuture<Void> headerFuture = CompletableFuture.allOf(task3, task5);
headerFuture.join();

//继续主线程

2、多个子线程,每个单独异步执行,不阻塞主线程

CompletableFuture<Void> task3 = CompletableFuture.runAsync(() -> { //TODO do something }, threadPoolExecutor);

CompletableFuture<Void> task5 = CompletableFuture.runAsync(() -> { //TODO do something }, threadPoolExecutor);

3、exceptionally使用,处理异常

CompletableFuture.runAsync(() -> { //TODO do something }, threadPoolExecutor).exceptionally(e -> {
    if (e instanceof CompletionException || e instanceof ExecutionException) {
        if (e.getCause() != null) {
            e = e.getCause();
        }
    }
    return MxtyDdlCompletableDTO.builder().throwable(e).build();
});

4、多个子线程执行完成,统计处理结果

List<CompletableFuture<MxtyDdlCompletableDTO>> futureList = new ArrayList<>();
for (List<String> list : partition) {
    CompletableFuture<MxtyDdlCompletableDTO> future = CompletableFuture.supplyAsync(() -> {
        tableCountNumSql(dqlDecorator, list, map);
        return MxtyDdlCompletableDTO.builder().build();
    }, ddlTaskPool).exceptionally(e -> {
        if (e instanceof CompletionException || e instanceof ExecutionException) {
            if (e.getCause() != null) {
                e = e.getCause();
            }
        }
        return MxtyDdlCompletableDTO.builder().throwable(e).build();
    });
    futureList.add(future);
}

5、指定线程池执行

public void addBzCache(List<String> sjyIdList) {
        //获取需要缓存标准元数据的数据
        List<BzZjgzVO> bzZjgzList = bzysDao.getBzCacheList(sjyIdList);
        if (CollectionUtils.isEmpty(bzZjgzList)) {
            log.warn("addBzCache没有查到需要缓存的数据,sjyId: {}", JSON.toJSONString(sjyIdList));
            return;
        }
        List<CompletableFuture<Void>> futureList = new ArrayList<>();
        //按数据源id分组
        Map<String, List<BzZjgzVO>> sjyMap =
                bzZjgzList.stream().collect(Collectors.groupingBy(BzZjgzVO::getSjyId));

        for (Map.Entry<String, List<BzZjgzVO>> en : sjyMap.entrySet()) {
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                try{
                    log.warn("addBzCache写入缓存数据,sjyId: {}", en.getKey());
                }catch (Exception e){
                    // 记录日志,避免异常被吞掉
                    log.error("addBzCache往redis写标准缓存数据失败,sjyId: {}", en.getKey(), e);
                }
            }, bzCacheThreadExecutor);
            futureList.add(future);
        }
        // 等待所有异步任务完成
        CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0])).join();
    }

方法1:

for (CompletableFuture<MxtyDdlCompletableDTO> future : futureList) {
    MxtyDdlCompletableDTO dto = future.join();
    if (dto == null || dto.getThrowable() != null) {

    }
}

方法2

List<MxtyDdlCompletableDTO> ddTOList =
        futureList.stream()
                .map(CompletableFuture::join)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

方法3

  // 等待所有异步任务完成
        CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0])).join();

CompletableFuture介绍

CompletableFuture是Java中的一个类,表示异步计算的未来结果。它是java.util.concurrent包的一部分,作为Future接口的增强功能在Java 8中引入的。
CompletableFuture类的一些关键特性包括:
1. 异步执行:CompletableFuture允许您异步执行任务,这意味着调用线程可以在不等待任务完成的情况下继续执行。
2. 完成阶段:CompletableFuture引入了CompletionStage的概念,它表示可能最终完成并返回值或异常的计算阶段。CompletionStage提供了将多个阶段链接在一起并定义它们之间依赖关系的方法。
3. 回调和可组合性:CompletableFuture支持回调,在未来完成时执行。您可以使用 `thenApply()` 、 `thenAccept()` 和 `thenRun()` 等方法附加回调。此外,CompletableFuture还提供了 `thenCompose()` 和 `thenCombine()` 等方法,用于组合多个未来。
4. 异常处理:CompletableFuture允许您处理计算过程中发生的异常,使用 `exceptionally()` 和 `handle()` 等方法。这些方法提供了处理和恢复异常的灵活性。
5. 异步组合器:CompletableFuture提供了一组称为"组合器"的方法,允许您组合多个未来、等待它们全部完成或选择第一个完成的未来。
总的来说,CompletableFuture为Java中的异步计算提供了强大而灵活的方式,使您能够编写更高效和响应性的代码。

本文章已经生成可运行项目
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暖兔兔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值