CountDownLatch介绍
CountDownLatch 是多线程控制的一种工具,它被称为 门阀、 计数器或者 闭锁
。它的核心思想是允许一个或多个线程等待其他线程完成操作。这个工具经常用来用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。
CountDownLatch 能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。它相当于是一个计数器,这个计数器的初始值就是线程的数量(n),每当一个任务完成后,计数器的值就会减一,当计数器的值为 0 时,表示所有的线程都已经任务了,在 CountDownLatch 上 await 的线程就会被唤醒,然后在 CountDownLatch 上等待的线程就可以恢复执行接下来的任务。
特性
- 一次性使用:CountDownLatch 的计数器不能被重置或重复使用。
- 线程安全:所有的方法都是线程安全的。
- 只递减,不递增:计数器只能通过调用
countDown()
减少,不能增加。
有一点要说明的是CountDownLatch初始化后计数器值递减到0的时候,不能再复原的,这一点区别于Semaphore,Semaphore是可以通过release操作恢复信号量的。(后面会讲)
应用场景
- 并发任务协调:多个线程并行执行,主线程等待所有子线程完成后再执行后续操作。
- 服务启动检查:一个服务需要依赖多个子组件或子服务的启动。当一个服务启动时,同时会加载很多组件和服务,这时候主线程会等待组件和服务的加载。当所有的组件和服务都加载完毕后,主线程和其他线程在一起完成某个任务。
- 并行计算结果合并:将任务分割为多个部分并行计算,等待所有部分完成后合并结果。
使用方法
CountDownLatch 的使用方法非常简单,主要调用两个方法即可: countDown()
和 await()
。
1. void countDown()
功能:将计数器减 1(线程任务完成后调用)。
使用规范:
- 每个线程在完成其任务后必须调用此方法。
countDown()
可以在任意线程中调用,并不要求调用它的线程和调用await()
的线程相同。
示例:
1 | CountDownLatch latch = new CountDownLatch(3); |
await 方法
CountDownLatch 中的 await 方法有两种,一种是不带任何参数的 await(),一种是可以等待一段时间的await(long timeout, TimeUnit unit)。下面我们先来看一下 await() 方法。
2. void await()
功能:使调用线程进入等待状态,直到计数器减到 0 或被中断。
使用规范:
- 调用
await()
的线程会阻塞,直到count
为 0。 - 如果线程被中断,则会抛出
InterruptedException
。
示例:
1 | CountDownLatch latch = new CountDownLatch(1); |
3. boolean await(long timeout, TimeUnit unit)
功能:与 await()
类似,但允许设置超时时间。如果计数器未归零,线程会在超时后继续执行。
使用规范:
- 返回值:
true
表示计数器归零,false
表示超时。 - 对于需要限制等待时间的场景非常有用,如超时保护。
示例:
1 | CountDownLatch latch = new CountDownLatch(1); |
4. long getCount()
功能:返回当前计数器的值。
使用技巧:
- 可用于监控任务的进度,特别是需要动态显示剩余任务数的场景。
示例:
1 | CountDownLatch latch = new CountDownLatch(5); |
5. String toString()
功能:返回包含计数器值的字符串。
用途:可用于日志记录或调试。
示例:
1 | CountDownLatch latch = new CountDownLatch(3); |
使用技巧和注意事项
一次性使用:
CountDownLatch 是一次性工具,使用后计数器不可重置。如果需要重复使用,请考虑使用 CyclicBarrier。避免死锁:
确保所有countDown()
都会被调用,否则await()
永远不会被唤醒,导致死锁。**多线程调用
countDown()
**:
多个线程可以同时调用countDown()
,无需担心线程安全问题。适配线程池:
使用线程池时,可以将任务分配给线程池的线程执行,减少线程开销。结合其他同步工具:
CountDownLatch 可以与 ExecutorService 或 Semaphore 结合使用,以实现更复杂的线程控制逻辑。
常见对比
Q1: CountDownLatch 和 CyclicBarrier 的区别?
- CountDownLatch:
- 用于一个或多个线程等待其他线程完成任务。
- 一次性使用。
- CyclicBarrier:
- 用于一组线程互相等待,直到到达公共屏障点。
- 可重复使用。
Q2: CountDownLatch 和 Semaphore 的区别?
- CountDownLatch:
- 主要用于线程之间的通知。
- 计数器只能减不能加。
- Semaphore:
- 用于限制资源访问的线程数。
- 可以通过
release()
方法增加信号量。
应用
调用3个线程实现轮流打印数字从1至100:
1 | import java.util.concurrent.CountDownLatch; |
参考
https://github.com/crisxuan/bestJavaer
https://www.cnblogs.com/cxuanBlog/p/14166322.html
About this Post
This post is written by Rui Xu, licensed under CC BY-NC 4.0.