Java并发工具:CountDownLatch

createh51周前 (05-16)技术教程5

CountDownLatch是Java并发包(java.util.concurrent)中提供的一种同步工具,用于控制一个或多个线程等待其他线程完成操作。它是一个非常有用的工具类,常用于协调多个线程之间的执行顺序。

基本概念

CountDownLatch 内部维护了一个计数器,这个计数器表示需要等待的事件数量:

- 调用 countDown() 方法会将计数器减 1。

- 调用 await() 方法的线程会一直阻塞,直到计数器变为 0。

常用方法

方法名

描述

CountDownLatch(int count)

构造函数,初始化计数器为 count

void await()

当前线程等待,直到计数器变为 0

boolean await(long timeout, TimeUnit unit)

等待一段时间后超时返回,避免永久阻塞

void countDown()

计数器减 1

long getCount()

获取当前计数器的值(调测程序用)

实现原理

  • 基于AQS共享模式

内部通过继承
AbstractQueuedSynchronizer(AQS)的Sync类实现,使用state变量表示当前计数值。

  • 原子操作保证线程安全

countDown()方法通过CAS(Compare-and-Swap)操作原子性减少计数器值。

  • 一次性特性

计数器归零后无法重置,若需重复使用同步机制,应选择CyclicBarrier。

使用场景

场景一:主线程等待所有子线程完成任务后再继续执行

import java.util.concurrent.CountDownLatch;

public class LatchExample {
    public static void main(String[] args) throws InterruptedException {
        int numThreads = 5;
        CountDownLatch latch = new CountDownLatch(numThreads);

        for (int i = 1; i <= numThreads; i++) {
            final int threadNum = i;
            new Thread(() -> {
                try {
                    System.out.println("Thread " + threadNum + " is working...");
                    Thread.sleep(1000); // 模拟工作
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    latch.countDown(); // 完成后减少计数器
                }
            }).start();
        }

        latch.await(); // 主线程等待所有线程完成
        System.out.println("All threads have finished. Main thread continues.");
    }
}

执行结果

Thread 3 is working...
Thread 5 is working...
Thread 2 is working...
Thread 1 is working...
Thread 4 is working...
All threads have finished. Main thread continues.

场景二:多个线程等待一个启动信号

比如多个线程准备就绪后同时开始执行:

import java.util.concurrent.CountDownLatch;

public class StartTogetherExample {
    public static void main(String[] args) {
        CountDownLatch startSignal = new CountDownLatch(1);

        for (int i = 1; i <= 3; i++) {
            final int threadNum = i;
            new Thread(() -> {
                try {
                    System.out.println("Thread " + threadNum + " is ready.");
                    startSignal.await(); // 所有线程等待启动信号
                    System.out.println("Thread " + threadNum + " starts working.");
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }).start();
        }

        try {
            Thread.sleep(2000); // 主线程模拟延迟后发出启动信号
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        startSignal.countDown(); // 启动所有线程
    }
}

执行结果

Thread 3 is ready.
Thread 1 is ready.
Thread 2 is ready.
Thread 3 starts working.
Thread 1 starts working.
Thread 2 starts working.

注意事项

- CountDownLatch是一次性使用的,一旦计数器减到 0,就不能再重置。

-如果你需要重复使用的类似功能,请考虑使用 CyclicBarrier。

- await() 方法可以被中断,建议捕获 InterruptedException 并做适当处理。

-避免资源泄漏:确保countDown()在finally块中调用,防止任务异常终止导致计数器未递减。

-超时机制:可结合await(long timeout, TimeUnit unit)设置超时时间,避免无限等待。

小结

功能

类比

多个线程等待某个条件满足后一起执行

运动员等裁判发令枪响才起跑

主线程等待多个子线程完成再继续

老师等所有学生交卷才能统计成绩

相关文章

java算法题-在区间范围内统计奇数数目

在leetcode(https://leetcode-cn.com/)上看到一道有趣的算法题:给你两个非负整数 low 和 high 。请你返回 low 和 high 之间(包括二者)奇数的数目。示例...

JVM 深度解析:运行时数据区域、分代回收与垃圾回收机制全攻略

共同学习,有错欢迎指出。JVM 运行时数据区域1. 程序计数器程序计数器是一块较小的内存空间,可看作当前线程所执行的字节码的行号指示器。在虚拟机概念模型里,字节码解释器通过改变这个计数器的值选取下一条...

图解常见的限流算法(计数器、滑动窗口计数、漏桶、令牌桶)

哈喽,大家好呀,我是呼噜噜,好久没有更新文章了,今天我们来聊聊在企业级项目中,常见的几种限流手段的原理及其实现什么场景需要限流随着互联网的业务发展,比如秒杀、双十一、618等这些我们耳熟能详,也有被人...

Java高并发解决方案:轻松应对海量请求

Java高并发解决方案:轻松应对海量请求在当今互联网时代,高并发问题已经成为每个Java开发者绕不开的话题。无论是电商平台的大促活动,还是社交平台的热门话题讨论,都可能瞬间产生海量请求。那么,我们该如...

踩坑!Java集合必学技能:Collection.size()方法深度解析与避坑

一、开发中遇到的问题:动态集合统计的"陷阱"在实际开发中,我们经常需要统计集合中的元素数量。例如,电商订单处理场景中需要实时统计待处理订单数。但如果不理解size()方法的底层逻辑,容...