CountDownLatch 是作为辅助类帮助我们进行并发编程;
CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。可以控制线程等待其他线程执行完成在执行;
使用方法:
final CountDownLatch countDownLatch = new CountDownLatch(2);
建立CountDownLatch 对象,CountDownLatch 只提供了一个构造器然后传入希望在多少次数之后执行;
countDownLatch .countDown();
这是用来计数减少1
latch.await();
线程会被挂起,用来等待计数为0再开始执行;
源码分析:
在调用构造器实例化对象时会给
private final Sync sync 创建对象;
对象继承自抽象类AbstractQueuedSynchronizer,AbstractQueuedSynchronizer继承自抽象类
AbstractOwnableSynchronizer。
初始构造时是给AbstractQueuedSynchronizer的 private volatile int state 赋以计数值;
当调用countDown方法时实际调用AbstractQueuedSynchronizer里的releaseShared 方法;在这个方法里调用Sync里的tryReleaseShared方法,里面进行死循环,在循环里开始获得计数值,不为0则进行更新值的操作,更新值主要是用Unsafe类中的方法来保证并发的安全性,unsafe实例会判断类加载器,我们自己用的话会有问题,jdk源码里使用不会有这问题。
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
stateOffset 是使用反射获取的类中这个变量内存即计数值中在该对象中的偏移量;第三个参数为期待的值,第四个为更新后的值。通过死循环来保证修改参数为期待值。失败则循环,除非修改成功则退出循环;当修改后计数值为0时,doReleaseShared方法里找到信号量即waitStatus为1的,将其置为0,并寻找下一个节点,设置其线程LockSupport.unpark,本质上是UNSAFE.unpark(thread)
调用await方法时,调用AbstractQueuedSynchronizer里的acquireSharedInterruptibly方法,调用sync里面的tryAcquireShared方法,判断计数器是否为0,为0则直接线程继续执行,不为0则调用AbstractQueuedSynchronizer的doAcquireSharedInterruptibly,调用addWaiter 将Node.SHARED,在方法内判断tail为null进入enq方法,调用compareAndSetHead为了Unsafe类中的方法更新head为空的Node对象,将它赋值给tail,总的来说addWaiter方法是为了tail为空则初始化tail,不为空则设置head值为空的Node对象设置tail为之前根据线程新建的Node并将其放在head后(即链表),返回tail值即现在链表尾Node对象;addWaiter技术进行死循环,检测是否为head值,在检测计数是否结束,拿到当前Node对象上一个节点,并判断等待状态,进行信号量的设置,下一步使用LockSupport的park方法(实质上是 UNSAFE.park(false, 0L)方法)。
总结:
这次源码解析对自己多线程开发有了一定了解,并且理解更多关于unsafe类。CountDownLatch大量使用了unsafe的方法加上循环来保证数据的修改和强一致性。链表是为了方便多个线程的添加,且多个线程按添加线程顺序调用。用空的head进行判断,一旦成功就开始依次进行调用。