源码解析-CountDownLatch
CountDownLatch 作为JUC并发包下一重要工具
Latch的意思是栅栏,作为一个阀门用来控制多个线程的
常用方法: new CountDownLatch(参数为放开栅栏的条件); countDown(); 条件-1 await(); 条件未达到0则阻塞住,达到0则放开栅栏
两个简单的应用场景
场景一 五个线程必须都执行完,才能继续往下,任一个线程没执行完都会await()处阻塞住
场景二 countDownLatch02.await()阻塞在每个线程内部 保证了每个线程都初始化完成,在同一起跑线进行下一步。countDownLatch01的作用同上
源码分析
老套路了,CountDownLatch下定义了一个内部类Sync 继承自AQS
so 还是AQS思想
大体思想就是:new CountDownLatch(n) AQS的state设置为n
每次CountDown() state-1 线程await()时判断state是否为0,如果不为0就加入到AQS的队列,然后将线程挂起。当state==0时 将线程从队列中一个一个唤醒
OK 到这里就可以结束了,下边是具体源码分析。
1. new CountDownLautch(参数) 如上所述 没啥好说的
2. await()方法
acquiresharedInterruptibly(参数传1) 参数并没什么意义
如果当前state不为0,栅栏没有满足打开的条件,则将线程加入队列并挂起
3. tryAcquireShared() 用来判断当前state是否为0
4. doAcquireSharedInterruptibly() 将线程封装成节点,加入到队列,然后挂起等待唤醒或中断
将线程封装成节点,加入到队列; //可以参考https://www.cnblogs.com/ttaall/p/13828134.html 这篇中的addWaiter()部分
boolean failed = true;
try {
for(死循环) {
获取当前节点的前置节点p;
if(如果前置节点是头节点) {
判断当前是否state==0;
如果栅栏可以打开{
唤醒头节点并传播下去;
failed = false;
return; //一切顺利并返回
}
}
if (线程是否应该挂起 并挂起 ){
shouldParkAfterFailedAcquire() //线程是否应该挂起
parkAndCheckInterrupt() //线程挂起,如果被中断则返回true
抛中断异常
}
}
}finally {
if (如果中途被中断了){
解散节点,不玩了
}
}
5. setHeadAndPropagate()
Node h 用来保存头节点
将当前节点设置为头节点;
if (满足条件){
Node s = 当前节点的下一个节点;
if (s是Null || s的下一个节点需要唤醒){
持续唤醒()
}
}