为了做下测试,我使用了上面这个例子,虽然没有用到线程但是原理还是一样,CountDownLatch的原理和适用场景大家应该都知道,就是主线程等其它线程都完成之后再继续执行。废话不多说,看源码。先从构造函数开始
CountDownLatch countDownLatch = new CountDownLatch(2);
如果传进的数值小于0,抛出异常。这里有个Sync对象,点进去看下是什么??
它是CountDownLatch的一个静态内部类,但是继承了AQS,它的构造函数是个setState(int count)方法。点进去
这个方法是在AQS内部实现的,也就是将我们传进来的值赋给一个state的变量。构造函数解析完了,现在来看下下面这个方法
countDownLatch.countDown();
这个方法原理就是将计数器减 1,我们看下源码是怎么实现的。
又调用了一层方法,继续跟进去
提一嘴,这个arg = 1,在上面方法有的。这里先看下这个if的判断条件,点进去
它是在CountDownLatch类实现的,这里是个死循环,首先获取state的值,记得在初始化CountDownLatch的时候我们传进去的值是赋给了这个变量state,然后进行判断,如果 state(也就是图中的int c) = 0,直接返回false,如果不为0,那么使用CAS来进行减1操作,最后判断 这个state在减1之后是否还是等于0。这里会有三种情况
第一种:如果此时state =0 ,但是此时还有其他线程执行到这里了,所以直接返回 false
第二种:此时state不等于0,但是有一个线程执行到这了,在减1之后等于0,那么返回true
第三种:此时state不等于0,但是有一个线程执行到这了,在减1之后不等于0,那么返回false。
现在回到下面这个方法
如果是返回false的是什么都不做,如果返回true,也就是上面第二种情况,就是state已经等于0了。进入到doReleaseShared()方法中。
在debug模式下发现进入到这个方法之后,head = null,所以会直接执行 break;也就是什么都不做。然后返回。接下来就是执行
countDownLatch.await();
还是进入这个方法的源码
继续跟进去
首先判断主线程是否是被中断的,如果是抛出异常。 如果没有进入if的判断,看下这个if判断了什么
如果state==0,那么返回1,也就意味着所有线程都执行完了,如果大于0,那么返回 -1,也就意味着还有线程没有执行完,
为什么会有这样的一个判断呢??? 因为线程的执行是具有随机性的,有可能你的其它线程还没有执行完,也就是state还不等于0,但是主线程已经执行到await()这个方法了。这个相信大家是能理解的,所以,如果此时state=0,以为着其它线程都执行完了,那么主线程自然可以继续往下执行。但是如果此时state > 0,意味着还有线程没有处理完任务,那么就会执行doAcquireSharedInterruptibly()这个方法了
首先看下
final Node node = addWaiter(Node.SHARED);
这里的Thread.currentThread()在这里就是main线程,如果不信大家把我们测试用例换成下面这样
你再进debug模式就可以知道了。这个方法的目的就是将主线程放到一个队列中去。然后返回当前这Node。
之前我们分析过,如下图
只有当state不等于0,并且主线程执行到了才会执行到下面这个方法
所以第一次肯定是会执行第二个 if 的语句块的。我们重点看下第二个判断
LockSupport.park()就是阻塞当前线程的。所以主线程是不能继续执行下去的。那又是什么时候唤醒的呢?? 看下上面的 setHeadAndPropagate()方法
最后还是用LockSupport.unpach()方法唤醒的,所以主线程就可以继续往下执行