搬砖日记-CountDownLatch和CompletableFuture的使用

本文介绍了CountDownLatch和CompletableFuture在并发编程中的应用。CountDownLatch用于主线程等待子线程完成,适合多模块数据加载场景;CompletableFuture支持异步执行和流式调用,适用于高并发环境。通过实例代码展示了它们的使用方法,帮助读者深入理解这两个并发工具类。

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

搬砖日记-CountDownLatch和CompletableFuture的使用

前言

不知不觉在大厂搬砖快一年了,在这一年里不得不说我学到了很多,特别把之前学到的知识给落地,这给我带来一些满足感和充实感,于是我想着抽空把最近学到的知识给整理整理,既是温习回顾还是一种分享,本篇文章将带领大家了解一下CountDownLatchCompletableFuture的使用.

正文

CountDownLatch

CountDownLatch程序计数器:

  • CountDownLatch用于主线程等待其他子线程任务都执行完毕后再执行.
  • CountDownLatch是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了.
  • 使用的场景:如多模块数据加速加载、治理大批量数据下游接口超时等

CountDownLatch主要的方法有:

  • await() :调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
  • await(long timeout,TimeUnit unit):只不过等待一定的时间后count值还没变为0的话就会继续执行(适用允许丢失部分数据的场景)
  • countDown():将count值减1

事例demo

import com.google.common.collect.Lists;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class Test020 {

    /**
     * 线程池
     */
    private static final ExecutorService QUERY_POOL = new ThreadPoolExecutor(
            10, 10,
            60, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(10000),
            new ThreadPoolExecutor.DiscardPolicy());

    public static void main(String[] args) {

        long start = System.currentTimeMillis();
        try {
            List<Long> resultList = new ArrayList<>();
            CountDownLatch countDownLatch = new CountDownLatch(4);
            for (int i = 0; i < 4; i++) {
                QUERY_POOL.execute(() -> {
                    try {
                        resultList.addAll(dohandler());
                    } finally {
                        countDownLatch.countDown();
                    }
                });
            }
            countDownLatch.await();
            System.out.println("结果:" + resultList + "耗时:" + (System.currentTimeMillis() - start));
        } catch (Exception e) {
            System.out.println("发生异常");
        }
    }

    public static List<Long> dohandler() {
        try {
            Thread.sleep(2500);
            return Lists.newArrayList(123L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

运行结果:
在这里插入图片描述

CompletableFuture

CompletableFuture

  • CompletableFuture是对Future模式的应用即实现,支持流调用、异步执行,它支持完成后得到通知。
  • CompletableFuture默认的时候会使用ForkJoinPool池来执提供线程来执行任务.

ForkJoinPool 线程池在 JDK 8 加入,主要用法和之前的线程池是相同的,也是把任务交给线程池去执行,线程池中也有任务队列来存放任务,和之前的五种线程池不同的是,它非常适合执行可以分解子任务的任务,比如树的遍历,归并排序,或者其他一些递归场景。

在这里插入图片描述

CompletableFuture提供了四个静态方法用来创建对象:

  1. runAsync(Runnable runnable) :同步执行,使用默认线程池
  2. runAsync(Runnable runnable, Executor executor) :同步执行,手动线程池
  3. supplyAsync(Supplier supplier) :异步执行,使用默认线程池
  4. supplyAsync(Supplier supplier, Executor executor):异步执行,手动线程池

事例demo

  • CompletableFuture.get()会阻塞线程,所以get操作不要在任务分发循环体内进行,否则整个操作就不是多线程异步操作了.
import com.google.common.collect.Lists;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class Test019 {

    public static void main(String[] args) {

        long start = System.currentTimeMillis();
        try {
            List<Long> resultList = new ArrayList<>();
            List<CompletableFuture<List<Long>>> tmp = new ArrayList();
            for (int i = 0; i < 4; i++) {
                final CompletableFuture<List<Long>> resultFuture = CompletableFuture.supplyAsync(() -> {
                    return dohandler();
                });
                tmp.add(resultFuture);
            }

            for (CompletableFuture<List<Long>> tmp2 : tmp) {
                resultList.addAll(tmp2.get());
            }

            System.out.println("结果:" + resultList + "耗时:" + (System.currentTimeMillis() - start));
        } catch (Exception e) {
            System.out.println("发生异常");
        }
    }

    public static List<Long> dohandler() {
        try {
            Thread.sleep(2500);
            return Lists.newArrayList(123L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

运行结果:
在这里插入图片描述

总结

也许我们可以通过死记硬背的方式学到很多“八股文”,但是往往这样学来的,终究是纸上学来终觉浅,如果能在真实的场景下应用的话,那么才能对此更有体会.

在这里插入图片描述

### CountDownLatch 结合 CompletableFuture 使用方法 在 Java 并发编程中,`CountDownLatch` `CompletableFuture` 可以很好地结合起来处理复杂的并发场景。下面是一个具体的例子说明如何将两者结合使用。 #### 示例代码 假设有一个任务需要多个子任务完成后才能继续执行后续逻辑: ```java import java.util.concurrent.*; public class LatchAndFutureExample { public static void main(String[] args) throws InterruptedException { // 定义计数器初始值为3,表示有三个子任务 CountDownLatch latch = new CountDownLatch(3); // 创建一个带有自定义线程工厂的线程池 ExecutorService executor = Executors.newFixedThreadPool(3); // 存储所有的 completable futures var allFutures = new ArrayList<CompletableFuture<Void>>(); try { for (int i = 0; i < 3; ++i) { final int taskId = i; // 对于每一个子任务都创建一个新的 CompletableFuture 实例 CompletableFuture<Void> futureTask = CompletableFuture.runAsync(() -> { System.out.println("Executing task " + taskId); // 模拟耗时操作 try { Thread.sleep((long)(Math.random() * 1000)); } catch (InterruptedException e) {} // 当前子任务完成之后减少计数值 latch.countDown(); }, executor).thenRun(() -> { // 所有的 thenRun 都会在对应的 runAsync 完成后触发 System.out.println("Task " + taskId + " has finished."); }); // 添加到列表以便稍后统一处理结果 allFutures.add(futureTask); } // 主线程在此处等待直到所有子任务均已完成 latch.await(); // 统一处理各个 Future 的最终状态 CompletableFuture.allOf(allFutures.toArray(new CompletableFuture[0])).join(); System.out.println("All tasks have been completed."); } finally { executor.shutdown(); } } } ``` 此程序展示了如何利用 `CountDownLatch` 来同步一组并行运行的任务,并且通过 `CompletableFuture` 提供更强大的组合能力[^1]。 当涉及到更加复杂的应用场景时,比如某些情况下可能希望某个特定条件满足后再去启动其他依赖的操作,则可以考虑进一步探索 `CompletableFuture` 更多高级特性如 `completeOnTimeout`, `orTimeout` 等来增强灵活性响应速度[^2]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值