概述
CountDownLatch可以翻译为倒计数器,让主调用线程等待其他一些线程工作完成后,再继续运行。(相当于调用所有子线程join方法的效果)
主要有两种使用场景:
第一种设置两个信号,一个是启动信号,当控制线程发出信号以后,所有线程才开始工作,一个是全部完成的信号,当所有工作线程完成后,控制线程才继续工作。
另一个典型用法是将问题分为N个部分,每一部分用子线程跑,然后在锁存器上递减计数,当所有子线程都完成后,调用线程将能够继续运行。
例子
public static void main(String[] args) throws InterruptedException {
final int N = 3;
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(N);
for (int i = 0; i < N; ++i)
new Thread(new Worker(startSignal, doneSignal)).start();
doSomethingElse();
startSignal.countDown();
doSomethingElse();
doneSignal.await();
System.out.println(currentThread().getName() + ": end");
}
private static void doSomethingElse() {
System.out.println(currentThread().getName() + ": do something");
}
static class Worker implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
public Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
@Override
public void run() {
try {
startSignal.await();
doWork();
System.out.println(currentThread().getName() + ": finish work");
doneSignal.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void doWork() {
System.out.println(currentThread().getName() + ": is working");
}
}
运行结果:
main: do something
main: do something
Thread-1: is working
Thread-0: is working
Thread-2: is working
Thread-2: finish work
Thread-0: finish work
Thread-1: finish work
main: end
例子github地址
主线程设置了两个信号量,startSignal,doneSignal
startSignal设置的state为1,doneSignal设置的state为N需要等待的线程量
当主线程在main函数中调用startSignal的countDown方法后,所有的子线程就可以开始做自己的事了,当主线程调用doneSignal.await,开始等待,知道所有子线程完成doneSignal.countDown后,主线程继续执行。
可以想像成一个体育老师千米测试,所有学生准备就绪,等老师发出开始信号startSignal.countDown,学生们都甩开膀子跑了,老师走的千米测试的终点后,调用doneSignal.await,这时老师就要等所有学生全部跑完后,才能下课,开始自己的生活了。
CountDownLatch源码
CountDownLatch使用的是AQS的共享式占用锁,定义了一个内部类Sync继承AQS,不清楚AQS源码的需要先看下这篇文章
//只有一个入参是int的构造函数,用来初始化Sync同步器
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
//重写了父类尝试请求加锁的方法,判断只有state为0,返回正数,即请求加锁成功
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
// 无限循环CAS操作state的值
for (;;) {
int c = getState();
//说明已经释放过了,不需要再唤醒了
if (c == 0)
return false;
int nextc = c-1;
//当state值减为0以后,共享锁释放,唤起等待节点
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//调用上面的尝试加锁方法
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
await方法就是判断state的值,如果state值不为0,加锁失败,进入排队等待状态。等待其他线程释放锁。
public void countDown() {
sync.releaseShared(1);
}
countDown方法比较简单就是对state减1,当state等于0,唤起等待的线程。