原创

Java并发编程之CountDownLatch介绍及使用场景

温馨提示:
本文最后更新于 2024年05月15日,已超过 252 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

介绍

CountDownLatch是Java中一个非常有用的并发工具类,它能够帮助我们实现多线程间的协调和同步。

在并发编程中,我们经常会遇到这样的场景:一个线程执行某个逻辑依赖于其它一个或多个线程的执行结果,且根据结果不同而执行不同的分支逻辑,这就需要一种机制来实现线程之间的等待和通知。CountDownLatch就是为了满足这样的需求而设计的工具类之一,它提供了一种机制,它可以让一个或多个线程等待其它线程的结果而执行后续逻辑。

原理

CountDownLatchAbstractQueuedSynchronizer的一个简单扩展。AbstractQueuedSynchronizer是一个实现先进先出(FIFO)阻塞队列的基础框架。CountDownLatch利用该框架内部维护了一个共享计数器及阻塞队列,该计数器初始化时需设置一个正整数,标明需要等待的线程数量。每个线程在完成任务时都会调用countDown()方法来减小该计数器的值。当计数器的值变为0时,等待的线程就会被唤醒,继续执行后续的逻辑。

用法

CountDownLatch的主要方法有两个:countDown()await()

  • countDown(): 当线程完成了自己的任务后,调用该方法使用CAS来减小共享计数器state的值,当值变为0时唤醒阻塞队列内等待线程执行任务
  • await(): 调用线程会进入阻塞队列等待共享计数器state的值变为0时被唤醒执行自身任务

使用场景

CountDownLatch在实际应用中有着广泛的用途,例如:

  • N个小伙伴聚餐,当N个人都到达才开始点餐
  • A线程逻辑依赖于其它N个子线程结果,根据结果不同执行不同分支
  • A线程完成逻辑(比如初始化资源),唤醒其它等待线程同时开始执行
    ......

示例

public class CountDownLatchDemo {
    final static Logger logger = LoggerFactory.getLogger(CountDownLatchDemo.class);
    private static final CountDownLatch latch = new CountDownLatch(3);
    private static final List<Boolean> LIST = new ArrayList<>();
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 3; i++) {
            Thread thread = new Thread(() -> {
                try {
                    logger.info("{} 线程模拟任务执行", Thread.currentThread().getName());
                    LIST.add(Math.random() < 0.5);
                } finally {
                    logger.info("{} 线程执行任务完成,计数器减1", Thread.currentThread().getName());
                    latch.countDown();
                }
            });
            thread.setName("工作线程" + i);
            thread.start();
        }
        latch.await();
        int count = 0;
        for (Boolean result : LIST) {
            if (result){
                count++;
            }
        }
        logger.info("{} 个线程完成了任务,主线程执行逻辑...", count);
    }
}

日志

public static void main(String[] args) throws InterruptedException {
    for (int i = 0; i < 3; i++) {
        Thread thread = new Thread(() -> {
            try {
                logger.info("{} 正赶往餐厅", Thread.currentThread().getName());
                Thread.sleep((long) (Math.random() * 1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                logger.info("{} 到达餐厅,计数器减1", Thread.currentThread().getName());
                latch.countDown();
            }
        });
        thread.setName("伙伴" + i);
        thread.start();
    }
    latch.await();
    logger.info("小伙伴都到达,开始点菜...");
}

日志

public static void main(String[] args) {
    CountDownLatch startSignal = new CountDownLatch(1);
    for (int i = 0; i < 3; i++) {
        Thread thread = new Thread(() -> {
            try {
                startSignal.await();
                logger.info("{} 线程开始执行任务", Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.setName("子线程" + i);
        thread.start();
    }
    logger.info("主线程开始执行任务");
    logger.info("主线程初始化资源");
    startSignal.countDown();
}

日志

总结

CountDownLatch是Java中一个强大的并发工具类,通过它我们可以轻松实现多线程之间的同步。本文介绍了CountDownLatch的原理、用法以及常见的应用场景,希望能够帮助读者更好地理解和使用该工具类。

正文到此结束
本文目录