Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说CountDownLatch原理简介和使用过程,希望能够帮助你!!!。
本文介绍下面试的高能考点 countDownLatch 的原理和应用
如并行计算,当某个处理的运算量很大时,可以将该运算任务拆分成多个子任务,等待所有的子任务都完成之后,父任务再拿到所有子任务的运算结果进行汇总。
这个示例包含了 线程池和CountDownLatch 的内容 ,更加符合实际使用场景
java.util.concurrent.CountDownLatch
分析源码的过程中大家把关注点放在我截图上圈红的地方 然后咱们慢慢的一点一点的把源码看完
这里将count值设置给state变量 调用了AQS类中setState方法
这个变量被volatile修饰
保证了这个变量 具有
1、可见性
2、有序性 防止指令重排序
3、原子性
这里先简单介绍下可见性
接着说CountDownLatch源码分析
在初始化CountDownLatch的时候会实例化这个继承了AQS的内部类Sync并且将count传给AQS的state变量
countDownLatch的await方法 调用了 AQS的acquireSharedInterruptibly方法并且传入了参数1 接下来对该方法分析
『tryAcquireShared』
在共享模式下尝试获取。这个方法需要查询是否对象的状态允许在共享模式下被获取,如果允许则去获取它。
这个方法总是被线程执行获取共享锁时被调用。如果这个方法报告失败,那么获取方法可能会使线程排队等待,如果它(即,线程)还没入队的话,直到其他的线程发出释放的信号。
默认实现抛出一个“UnsupportedOperationException”
返回:
a)< 0 : 一个负数的返回表示失败;
b) 0 : 0表示在共享模式下获取锁成功,但是后续的获取共享锁将不会成功
c)> 0 : 大于0表示共享模式下获取锁成功,并且后续的获取共享锁可能也会成功,在这种情况下后续等待的线程必须检查是否有效。
看下AQS的子类Sync的tryAcquireShared方法的实现
这个逻辑过程中使用了大量的CAS来进行原子性的修改,当修改失败的时候,是会通过for(;;)来重新循环的,也就是说『doAcquireSharedInterruptibly』使用自旋锁(自旋+CAS)来保证在多线程并发的情况下,队列节点状态也是正确的以及在等待队列的正确性,最终使得当前节点要么获取共享锁成功,要么被挂起等待唤醒
我们需要一个通知信号,主要是因为当前线程要被挂起了(park)。
而如果waitStatus已经是’SIGNAL’的话就无需修改,直接挂起就好,
而如果waitStatus是’CANCELLED’的话,说明prev已经被取消了,是个无效节点了,那么无需修改这个无效节点的waitStatus,而是需要先找到一个有效的prev。
因此,剩下的情况就只有当waitStatus为’0’和’PROPAGAET’了(注意,waitStatus为’CONDITION’是节点不在等待队列中,所以当下情况waitStatus不可能为’CONDITION’),这时我们需要将prev的waitStatus使用CAS的方式修改为’SIGNAL’,而且只有修改成功的情况下,当前的线程才能安全被挂起。
还值得注意的时,因此该方法的CAS操作都是没有自旋的,所以当它操作完CAS后都会返回false,在外层的方法中会使用自旋,当发现返回的是false时,会再次调用该方法,以检查保证有当前node有一个有效的prev,并且其waitStatus为’SIGNAL’,在此情况下当前的线程才会被挂起(park)。
双向链表数据结构
源码分析未完待续 下篇文章继续分析
https://gitee.com/pingfanrenbiji/myconcurrent/tree/master/src/main/java/pers/hanchao/concurrent/eg14
https://www.jianshu.com/p/9ee0194d598c
今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
上一篇
已是最后文章
下一篇
已是最新文章