CountDownLatch类,从jdk1.5引入;是提供给开发的计数器。
如下给出示例:
package test;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchRunner {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(2);
//错误的位置;写在这里,程序会走不下去的
//countDownLatch.await();
for(int i = 0; i < 2; i++) {
new Thread(() -> {
System.out.println("计数器做减法!");
countDownLatch.countDown();
}).start();
}
countDownLatch.await();
System.out.println("计数器减为0了,后续。。。");
}
}
输出结果:
源码分析(基于jdk1.8)
CountDownLatch是基于AQS(指的是AbstractQueuedSynchronizer这个抽象类)共享模式;其内部定义了Sync类,继承自AQS,重写了tryAcquireShared(int acquires)、tryReleaseShared(int releases)这2个方法。
tryAcquireShared(int acquires)方法如下:
判断计数器的值是否为0,为0返回1,不为0返回-1。
tryReleaseShared(int releases)方法如下:
判断计数器的值是否为0,为0返回false,不为0将值减1并更新,再将更新后的值和0比较,为0返回true,否则返回false。
注:更新时,循环+compareAndSetXXX(),就是使用cas机制保证多线程的安全。
我们就分析示例代码,
CountDownLatch的构造函数:
设置计数器的值为传入的值。

CountDownLatch的await()方法:
只有一行代码:调用AQS的acquireSharedInterruptibly(int arg),
点进去看,
里面调用了Sync的tryAcquireShared(int acquires),如果返回值大于等于0(翻译一下,就是计数器的值为0),不做处理,否则调用AQS的doAcquireSharedInterruptibly(int arg);我们再看这个方法,
先去调用addWaiter(Node node),
方法中先new了一个Node对象(起个名字:对象a),对象a的thread属性为当前线程对象,nextWaiter属性为Node.SHARED对象,然后将pred变量赋值为tail对象,此时因为tail对象为null,进入到enq(Node node)方法中,
此方法会把head对象初始化好,赋值为一个new Node(),也会把tail对象赋值为对象a,此时enq(Node node)执行完,addWaiter(Node node)也执行完,返回对象a;doAcquireSharedInterruptibly(int arg)继续往下看,开始执行循环,拿到对象a的prev属性去和head对象比较,此时是相等的,进入到if中,去调用Sync的tryAcquireShared(int acquires),此时计数器的值不为0,不进入下一个if,往下执行,调用shouldParkAfterFailedAcquire(Node pred, Node node)方法中,
因为head对象的waitSatus为0(int属性,默认为0),此时赋值为Node.SIGNAL(-1),再返回false;此时条件未满足,再次执行循环,当再次执行shouldParkAfterFailedAcquire(Node pred, Node node)方法时,因为head对象的waitStatus为-1,返回true,会接着执行parkAndCheckInterrupt()方法,
执行到LockSupport#park(Object blocker)方法时,线程会阻塞,不再执行后续代码。
CountDownLatch的countDown()方法:
也是只有一行代码,
调用AQS的releaseShared(int arg),点进去看,
调用了Sync的tryReleaseShared(int releases),如果返回值为false(翻译一下,就是计数器的值为0或减去1后不为0),代码就执行完毕,否则(计数器的值减去1后为0)执行doReleaseShared()方法,细看这个方法,
执行循环,判断head对象是否为null并且head和tail对象是否相等,此时2个条件都满足,进入到if里,判断head对象的waitStatus属性,此时该值为Node.SIGNAL,接着进入到if里,利用cas机制把head对象的waitStatus属性更新为0,然后接着执行unparkSuccessor(Node node),
此时因为head对象有后继节点(next属性为上面的对象a),此时655行的if不执行,执行661行的if,调用LockSupport#unpark(Thread thread)方法,该方法会把上述阻塞的线程恢复,然后unparkSuccessor(Node node)执行完毕,接着往下执行,695行的if未满足(经作者调试后发现,unparkSuccessor(Node node)执行完后,head对象会赋值为上面的对象a,head、tail对象是同一个),再来一次循环,此时head对象不为null,但是head对象和tail对象相等,会执行695行的if,此时终止循环,doReleaseShared()方法执行完毕,countDown()方法也同样执行完毕。
最后,欢迎志同道合的朋友来评论区讨论。