前言:

JUC中提供了很多同步工具类,比如CountDownLatch、CyclicBarrier、Semaphore等,都可以作用同步手段来实现多线程之间的同步效果

一、CountDownLatch

1.1、CountDownLatch的使用

CountDownLatch可以理解为是同步计数器,作用是允许一个或多个线程等待其他线程执行完成之后才继续执行,比如打dota、LoL或者王者荣耀时,创建了一个五人房,只有当五个玩家都准备了之后,游戏才能正式开始,否则游戏主线程会一直等待着直到玩家全部准备。在玩家没准备之前,游戏主线程会一直处于等待状态。如果把CountDownLatch比做此场景都话,相当于开始定义了匹配游戏需要5个线程,只有当5个线程都准备完成了之后,主线程才会开始进行匹配操作。

CountDownLatch案例如下:

  1. public static void countDownLatchTest() throws Exception{
  2. CountDownLatch latch = new CountDownLatch(5);//定义了需要达到条件都线程为5个线程
  3. new Thread(new Runnable() {
  4. @Override
  5. public void run() {
  6. for (int i=0; i<12; i++){
  7. try {
  8. Thread.sleep(1000L);
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. new Thread(new Runnable() {
  13. @Override
  14. public void run() {
  15. long count = latch.getCount();
  16. latch.countDown();/**相当于准备游戏成功*/
  17. if(count > 0) {
  18. System.out.println("线程" + Thread.currentThread().getName() + "组队准备,还需等待" + latch.getCount() + "人准备");
  19. }else {
  20. System.out.println("线程" + Thread.currentThread().getName() + "组队准备,房间已满不可加入");
  21. }
  22. }
  23. }).start();
  24. }
  25. }
  26. }).start();
  27. new Thread(new Runnable() {
  28. @Override
  29. public void run() {
  30. try {
  31. System.out.println("游戏房间等待玩家加入...");
  32. /**一直等待到规定数量到线程都完全准备之后才会继续往下执行*/
  33. latch.await();
  34. System.out.println("游戏房间已锁定...");
  35. } catch (InterruptedException e) {
  36. e.printStackTrace();
  37. }
  38. }
  39. }).start();
  40. System.out.println("等待玩家准备中...");
  41. /**一直等待到规定数量到线程都完全准备之后才会继续往下执行*/
  42. latch.await();
  43. System.out.println("游戏匹配中...");
  44. }

执行结果如下:

  1. 等待玩家准备中...
  2. 游戏房间等待玩家加入...
  3. 线程Thread-2组队准备,还需等待4人准备
  4. 线程Thread-3组队准备,还需等待3人准备
  5. 线程Thread-4组队准备,还需等待2人准备
  6. 线程Thread-5组队准备,还需等待1人准备
  7. 线程Thread-6组队准备,还需等待0人准备
  8. 游戏匹配中...
  9. 游戏房间已锁定...
  10. 线程Thread-7组队准备,房间已满不可加入
  11. 线程Thread-8组队准备,房间已满不可加入
  12. 线程Thread-9组队准备,房间已满不可加入
  13. 线程Thread-10组队准备,房间已满不可加入
  14. 线程Thread-11组队准备,房间已满不可加入
  15. 线程Thread-12组队准备,房间已满不可加入
  16. 线程Thread-13组队准备,房间已满不可加入

本案例中有两个线程都调用了latch.await()方法,则这两个线程都会被阻塞,直到条件达成。当5个线程调用countDown方法之后,达到了计数器的要求,则后续再执行countDown方法的效果就无效了,因为CountDownLatch仅一次有效

1.2、CountDownLatch的实现原理

CountDownLatch的实现原理主要是通过内部类Sync来实现的,内部类Sync是AQS的子类,主要是通过重写AQS的共享式获取和释放同步状态方法来实现的。源码如下:

CountDownLatch初始化时需要定义调用count的次数,然后每调用一次countDown方法都会计数减一,源码如下:

  1. public CountDownLatch(int count) {
  2. if (count < 0) throw new IllegalArgumentException("count < 0");
  3. this.sync = new Sync(count);
  4. }
  1. public void countDown() {
  2. sync.releaseShared(1);
  3. }
  1. public void await() throws InterruptedException {
  2. sync.acquireSharedInterruptibly(1);
  3. }

可以看出CountDownLatch的实现逻辑全部都是调用内部类Sync的对应方法实现的,Sync源码如下:

  1. private static final class Sync extends AbstractQueuedSynchronizer {
  2. private static final long serialVersionUID = 4982264981922014374L;
  3.  
  4. Sync(int count) {
  5. /**初始化设置计数值实际就是设置AQS的同步状态值*/
  6. setState(count);
  7. }
  8.  
  9. int getCount() {
  10. return getState();
  11. }
  12.  
  13. protected int tryAcquireShared(int acquires) {
  14. return (getState() == 0) ? 1 : -1;
  15. }
  16.  
  17. /**CountDownLatch的countDown方法的实际执行逻辑*/
  18. protected boolean tryReleaseShared(int releases) {
  19. // Decrement count; signal when transition to zero
  20. for (;;) {
  21. int c = getState();
  22. /**当CountDownLatch的计数值为0时,表示倒计数已完成,则直接返回false*/
  23. if (c == 0)
  24. return false;
  25. int nextc = c-1;
  26. /**通过CAS操作来设置同步状态自减1操作*/
  27. if (compareAndSetState(c, nextc))
  28. /**返回当前同步状态值是否为0,只有当状态值为0时才返回true,否则返回false*/
  29. return nextc == 0;
  30. }
  31. }
  32. }

通过内部类Sync的源码可以分析出,CountDownLatch的实现完整逻辑如下:

1、初始化CountDownLatch实际就是设置了AQS的state为计数的值

2、调用CountDownLatch的countDown方法时实际就是调用AQS的释放同步状态的方法,每调用一次就自减一次state值

3、调用await方法实际就调用AQS的共享式获取同步状态的方法acquireSharedInterruptibly(1),这个方法的实现逻辑就调用子类Sync的tryAcquireShared方法,只有当子类Sync的tryAcquireShared方法返回大于0的值时才算获取同步状态成功,

否则就会一直在死循环中不断重试,直到tryAcquireShared方法返回大于等于0的值,而Sync的tryAcquireShared方法只有当AQS中的state值为0时才会返回1,否则都返回-1,也就相当于只有当AQS的state值为0时,await方法才会执行成功,否则

就会一直处于死循环中不断重试。

总结:

CountDownLatch实际完全依靠AQS的共享式获取和释放同步状态来实现,初始化时定义AQS的state值,每调用countDown实际就是释放一次AQS的共享式同步状态,await方法实际就是尝试获取AQS的同步状态,只有当同步状态值为0时才能获取成功。

二、CyclicBarrier

2.1、CyclicBarrier的使用

CyclicBarrier可以理解为一个循环同步屏障,定义一个同步屏障之后,当一组线程都全部达到同步屏障之前都会被阻塞,直到最后一个线程达到了同步屏障之后才会被打开,其他线程才可继续执行。

还是以dota、LoL和王者荣耀为例,当第一个玩家准备了之后,还需要等待其他4个玩家都准备,游戏才可继续,否则准备的玩家会被一直处于等待状态,只有当最后一个玩家准备了之后,游戏才会继续执行。

CyclicBarrier使用案例如下:

  1. public static void CyclicBarrierTest() throws Exception {
  2. CyclicBarrier barrier = new CyclicBarrier(5);//定义需要达到同步屏障的线程数量
  3. for (int i=0;i<12;i++){
  4. Thread.sleep(1000L);
  5. new Thread(new Runnable() {
  6. @Override
  7. public void run() {
  8. try {
  9. System.out.println("线程"+Thread.currentThread().getName()+"组队准备,当前" + (barrier.getNumberWaiting()+1) + "人已准备");
  10. barrier.await();/**线程进入等待,直到最后一个线程达到同步屏障*/
  11. System.out.println("线程:"+Thread.currentThread().getName()+"开始组队游戏");
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. } catch (BrokenBarrierException e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. }).start();
  19. }
  20. }

执行结果如下:

  1. 线程Thread-0组队准备,当前1人已准备
  2. 线程Thread-1组队准备,当前2人已准备
  3. 线程Thread-2组队准备,当前3人已准备
  4. 线程Thread-3组队准备,当前4人已准备
  5. 线程Thread-4组队准备,当前5人已准备
  6. 线程:Thread-4开始组队游戏
  7. 线程:Thread-0开始组队游戏
  8. 线程:Thread-1开始组队游戏
  9. 线程:Thread-2开始组队游戏
  10. 线程:Thread-3开始组队游戏
  11. 线程Thread-5组队准备,当前1人已准备
  12. 线程Thread-6组队准备,当前2人已准备
  13. 线程Thread-7组队准备,当前3人已准备
  14. 线程Thread-8组队准备,当前4人已准备
  15. 线程Thread-9组队准备,当前5人已准备
  16. 线程:Thread-9开始组队游戏
  17. 线程:Thread-5开始组队游戏
  18. 线程:Thread-7开始组队游戏
  19. 线程:Thread-6开始组队游戏
  20. 线程:Thread-8开始组队游戏
  21. 线程Thread-10组队准备,当前1人已准备
  22. 线程Thread-11组队准备,当前2人已准备

本案例中定义了达到同步屏障的线程为5个,每当一个线程调用了barrier.await()方法之后表示该线程已达到屏障,此时当前线程会被阻塞,只有当最后一个线程调用了await方法之后,被阻塞的其他线程才会被唤醒继续执行。

另外CyclicBarrier是循环同步屏障,同步屏障打开之后立马会继续计数,等待下一组线程达到同步屏障。而CountDownLatch仅单次有效。

2.2、CyclicBarrier的实现原理

先看下CyclicBarrier的构造方法

  1. public CyclicBarrier(int parties) {
  2. this(parties, null);
  3. }
  4.  
  5. public CyclicBarrier(int parties, Runnable barrierAction) {
  6. if (parties <= 0) throw new IllegalArgumentException();
  7. this.parties = parties;//同步屏障总需线程数
  8. this.count = parties;//当前剩余需要达到的线程数
  9. this.barrierCommand = barrierAction;
  10. }

CyclicBarrier的构造方法没有特殊之处,主要是给两个属性parties(总线程数)、count(当前剩余线程数)进行赋值,这里需要两个值的原因是CyclicBarrier提供了重置的功能,当调用reset方法重置时就需要将count值再赋值成parties的值

再看下await方法的实现逻辑

  1. public int await() throws InterruptedException, BrokenBarrierException {
  2. try {
  3. //调用私有方法dowait方法
  4. return dowait(false, 0L);
  5. } catch (TimeoutException toe) {
  6. throw new Error(toe); // cannot happen
  7. }
  8. }
  9.  
  10. /**
  11. * Main barrier code, covering the various policies.
  12. */
  13. private int dowait(boolean timed, long nanos)
  14. throws InterruptedException, BrokenBarrierException,
  15. TimeoutException {
  16. //CyclicBarrier有个ReentrantLock属性的lock
  17. final ReentrantLock lock = this.lock;
  18. //加锁操作
  19. lock.lock();
  20. try {
  21. final Generation g = generation;
  22.  
  23. if (g.broken)
  24. throw new BrokenBarrierException();
  25. //响应线程中断
  26. if (Thread.interrupted()) {
  27. breakBarrier();
  28. throw new InterruptedException();
  29. }
  30. //count自减操作
  31. int index = --count;
  32. //判断当前还需达到同步屏障的线程数是否为0
  33. if (index == 0) { // tripped
  34. boolean ranAction = false;
  35. try {
  36. //barrierCommand是同步屏障打开之后需要执行的Runnable对象
  37. final Runnable command = barrierCommand;
  38. if (command != null)
  39. //如果Runnable对象不为空直接执行Runnable线程任务
  40. command.run();
  41. ranAction = true;
  42. //本次同步屏障全部达成,唤醒所有线程并开始下一次同步屏障
  43. nextGeneration();
  44. return 0;
  45. } finally {
  46. if (!ranAction)
  47. breakBarrier();
  48. }
  49. }
  50.  
  51. // loop until tripped, broken, interrupted, or timed out
  52. for (;;) {
  53. try {
  54. if (!timed)
  55. //调用Condition对象的await方法使当前线程进入等待状态
  56. trip.await();
  57. else if (nanos > 0L)
  58. nanos = trip.awaitNanos(nanos);
  59. } catch (InterruptedException ie) {
  60. if (g == generation && ! g.broken) {
  61. breakBarrier();
  62. throw ie;
  63. } else {
  64. // We're about to finish waiting even if we had not
  65. // been interrupted, so this interrupt is deemed to
  66. // "belong" to subsequent execution.
  67. Thread.currentThread().interrupt();
  68. }
  69. }
  70.  
  71. if (g.broken)
  72. throw new BrokenBarrierException();
  73.  
  74. if (g != generation)
  75. return index;
  76.  
  77. if (timed && nanos <= 0L) {
  78. breakBarrier();
  79. throw new TimeoutException();
  80. }
  81. }
  82. } finally {
  83. lock.unlock();
  84. }
  85. }
  86.  
  87. private void nextGeneration() {
  88. // signal completion of last generation
  89. //唤醒所有线程
  90. trip.signalAll();
  91. // set up next generation
  92. count = parties;
  93. generation = new Generation();
  94. }

从源码可以看出CyclicBarrier的实现原理主要是通过ReentrantLock和Condition来实现的,主要实现流程如下:

1、创建CyclicBarrier时定义了CyclicBarrier对象需要达到的线程数count

2、每当一个线程执行了await方法时,需要先通过ReentrantLock进行加锁操作,然后对count进行自减操作,操作成功则判断当前count是否为0;

3、如果当前count不为0则调用Condition的await方法使当前线程进入等待状态;

4、如果当前count为0则表示同步屏障已经完全,此时调用Condition的signalAll方法唤醒之前所有等待的线程,并开启循环的下一次同步屏障功能;

5、唤醒其他线程之后,其他线程继续执行剩余的逻辑。

2.3、通过Synchronized和wait/notify实现CyclicBarrier

通过分析了解了CyclicBarrier是通过ReentrantLock和Condition来实现的,而ReentrantLock+Condition在使用上基本上等同于Synchronized+wait/notify,既然如此就可以通过Synchronized+wait/notify来自定义一个CyclicBarrier,话不多说,代码如下:

  1. public class MyCyclicBarrier {
  2.  
  3. public MyCyclicBarrier(int parties){
  4. this.parties = parties;
  5. this.count = parties;
  6. }
  7.  
  8. /**当前剩余数量*/
  9. private int count;
  10.  
  11. /**设置同数量*/
  12. private int parties;
  13.  
  14. /**获取当前已准备数量*/
  15. public int getNumberWaiting(){
  16. return parties-count;
  17. }
  18.  
  19. public int await(){
  20. synchronized (this){
  21. int rest = --count;
  22. if(rest==0){
  23. //当剩余数量为0时表示所有线程达到屏障,则重置同步屏障并唤醒所有线程
  24. this.count = parties;
  25. this.notifyAll();
  26. return 0;
  27. }
  28. try {
  29. //当剩余数量大于0时,线程进入等待状态
  30. this.wait();
  31. } catch (InterruptedException e) {
  32. e.printStackTrace();
  33. }
  34. }
  35. return count;
  36. }
  37.  
  38. /**测试 Main 方法*/
  39. public static void main(String[] args) throws InterruptedException {
  40. MyCyclicBarrier barrier = new MyCyclicBarrier(5);//定义需要达到同步屏障的线程数量
  41. for (int i=0;i<12;i++){
  42. Thread.sleep(1000L);
  43. new Thread(new Runnable() {
  44. @Override
  45. public void run() {
  46. try {
  47. System.out.println("线程"+Thread.currentThread().getName()+"组队准备,当前" + (barrier.getNumberWaiting()+1) + "人已准备");
  48. barrier.await();
  49. System.out.println("线程:"+Thread.currentThread().getName()+"开始组队游戏");
  50. } catch (Exception e) {
  51. e.printStackTrace();
  52. }
  53. }
  54. }).start();
  55. }
  56. }
  57. }

执行结果如下:

  1. 线程Thread-0组队准备,当前1人已准备
  2. 线程Thread-1组队准备,当前2人已准备
  3. 线程Thread-2组队准备,当前3人已准备
  4. 线程Thread-3组队准备,当前4人已准备
  5. 线程Thread-4组队准备,当前5人已准备
  6. 线程:Thread-4开始组队游戏
  7. 线程:Thread-3开始组队游戏
  8. 线程:Thread-0开始组队游戏
  9. 线程:Thread-1开始组队游戏
  10. 线程:Thread-2开始组队游戏
  11. 线程Thread-5组队准备,当前1人已准备
  12. 线程Thread-6组队准备,当前2人已准备
  13. 线程Thread-7组队准备,当前3人已准备
  14. 线程Thread-8组队准备,当前4人已准备
  15. 线程Thread-9组队准备,当前5人已准备
  16. 线程:Thread-9开始组队游戏
  17. 线程:Thread-7开始组队游戏
  18. 线程:Thread-5开始组队游戏
  19. 线程:Thread-6开始组队游戏
  20. 线程:Thread-8开始组队游戏
  21. 线程Thread-10组队准备,当前1人已准备
  22. 线程Thread-11组队准备,当前2人已准备

可以看出实现的效果和CyclicBarrier实现的效果完全一样

三、Semaphore

3.1、Semaphore的使用

Semaphore字面意思是信号量,实际可以看作是一个限流器,初始化Semaphore时就定义好了最大通行证数量,每次调用时调用方法来消耗,业务执行完毕则释放通行证,如果通行证消耗完,再获取通行证时就需要阻塞线程直到有通行证可以获取。

比如银行柜台的窗口,一共有5个窗口可以使用,当窗口都被占用时,后面来的人就需要排队等候,直到有窗口用户办理完业务离开之后后面的人才可继续争取。模拟代码如下:

  1. public static void semaphoreTest() throws InterruptedException {
  2. int count = 5;
  3. Semaphore semaphore = new Semaphore(count);
  4. System.out.println("初始化" + count + "个银行柜台窗口");
  5. for (int i=0;i<10;i++){
  6. Thread.sleep(1000L);
  7. new Thread(new Runnable() {
  8. @Override
  9. public void run() {
  10. try {
  11. System.out.println("用户"+Thread.currentThread().getName()+"占用窗口");
  12. semaphore.acquire(1);//获取许可证
  13. /**用户办理业务需要消耗一定时间*/
  14. System.out.println("用户"+Thread.currentThread().getName()+"开始办理业务");
  15. Thread.sleep(5000L);
  16. semaphore.release();//释放许可证
  17. System.out.println("用户"+Thread.currentThread().getName()+"离开窗口");
  18. } catch (Exception e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. }).start();
  23. }
  24. }

执行结果如下:

  1. 初始化5个银行柜台窗口
  2. 用户Thread-0占用窗口
  3. 用户Thread-0开始办理业务
  4. 用户Thread-1占用窗口
  5. 用户Thread-1开始办理业务
  6. 用户Thread-2占用窗口
  7. 用户Thread-2开始办理业务
  8. 用户Thread-3占用窗口
  9. 用户Thread-3开始办理业务
  10. 用户Thread-4占用窗口
  11. 用户Thread-4开始办理业务
  12. 用户Thread-0离开窗口
  13. 用户Thread-5占用窗口
  14. 用户Thread-5开始办理业务
  15. 用户Thread-1离开窗口
  16. 用户Thread-6占用窗口
  17. 用户Thread-6开始办理业务
  18. 用户Thread-2离开窗口
  19. 用户Thread-7占用窗口
  20. 用户Thread-7开始办理业务
  21. 用户Thread-3离开窗口
  22. 用户Thread-8占用窗口
  23. 用户Thread-8开始办理业务
  24. 用户Thread-4离开窗口
  25. 用户Thread-9占用窗口
  26. 用户Thread-9开始办理业务
  27. 用户Thread-5离开窗口
  28. 用户Thread-6离开窗口
  29. 用户Thread-7离开窗口
  30. 用户Thread-8离开窗口
  31. 用户Thread-9离开窗口 

可以看出前5个线程可以直接占用窗口,但是后5个线程需要等待前面的线程离开了窗口之后才可占用窗口。

Semaphore调用acquire方法获取许可证,可以同时获取多个,但是也需要对应的释放多个,否则会造成其他线程获取不到许可证的情况。一旦许可证被消耗完,那么线程就需要被阻塞,直到许可证被释放才可继续执行。

另外Semaphore还具有公平模式和非公平模式两种用法,公平模式则遵循FIFO原则先排队的线程先拿到许可证;非公平模式则自行争取。

3.2、Semaphore实现原理

Semaphore的构造方法

  1. public Semaphore(int permits) {
  2. //非公平模式
  3. sync = new NonfairSync(permits);
  4. }
  5.  
  6. public Semaphore(int permits, boolean fair) {
  7. //公平模式
  8. sync = fair ? new FairSync(permits) : new NonfairSync(permits);
  9. }

构造方法只有两个参数,一个是许可证总数量,一个是是否为公平模式;默认是非公平模式

Semaphore的实现全部是通过其内部类Sync来实现了,Sync也是AQS的子类,Semaphore的实现方式基本上和ReentrantLock的实现原理如出一辙。

公平模式实现原理:

  1. FairSync(int permits) {
  2. //初始化AQS的state的值
  3. super(permits);
  4. }
  5.  
  6. protected int tryAcquireShared(int acquires) {
  7. for (;;) {
  8. //当首节点的后继节点不是当前线程时直接return -1
  9. if (hasQueuedPredecessors())
  10. return -1;
  11. //获取当前AQS的state值
  12. int available = getState();
  13. //将state减去需要占用的许可证数量得到剩余的许可证数量
  14. int remaining = available - acquires;
  15. /**
  16. * 当remaining<0时返回remaining则表示获取许可证失败,会进入AQS的死循环不停重试
  17. * 当remain>0时,并且CAS成功了则返回remaining,表示获取许可证成功了
  18. * */
  19. if (remaining < 0 ||
  20. compareAndSetState(available, remaining))
  21. return remaining;
  22. }
  23. }

公平模式就是当当前线程是AQS同步队列首节点的后继节点时才有权利尝试获取共享式的同步状态,并将同步状态值减去需要占用的许可证数量,如果剩余许可证数量小于0则表示获取失败进入AQS的死循环不停重试;

如果许可证数量大于0并且CAS设置成功了,则返回剩余许可证数量表示抢占许可证成功;

非公平模式实现原理:

看我公平模式的实现基本是就可以猜到非公平模式是如何实现的,只是会少了一步判断当前节点是否是首节点的后继节点而已。

  1. static final class NonfairSync extends Sync {
  2. private static final long serialVersionUID = -2694183684443567898L;
  3.  
  4. NonfairSync(int permits) {
  5. super(permits);
  6. }
  7.  
  8. protected int tryAcquireShared(int acquires) {
  9. return nonfairTryAcquireShared(acquires);
  10. }
  11. }
  12.  
  13. final int nonfairTryAcquireShared(int acquires) {
  14. for (;;) {
  15. int available = getState();
  16. int remaining = available - acquires;
  17. if (remaining < 0 ||
  18. compareAndSetState(available, remaining))
  19. return remaining;
  20. }
  21. }

了解完Semaphore的公平模式和非公平模式的占有许可证的方法,再分析释放许可证的方法,不过可以先自行猜测下会是如何实现的,既然获取许可证是通过state字段不断减少来实现的,那么毫无疑问释放许可证就肯定是不断给state增加来实现的。

释放许可证源码如下:

  1. /**尝试释放许可证*/
  2. protected final boolean tryReleaseShared(int releases) {
  3. for (;;) {
  4. int current = getState();
  5. int next = current + releases;
  6. if (next < current) // overflow
  7. throw new Error("Maximum permit count exceeded");
  8. if (compareAndSetState(current, next))
  9. return true;
  10. }
  11. }

Semaphore的释放许可证实际就是调用AQS的共享式释放同步状态的方法,然后调用内部类Sync重写的AQS的tryReleaseShared方法,实现逻辑就是不停CAS设置state的值加上需要释放的数量,直到CAS成功。这里少了AQS的逻辑解析,有兴趣可自行回顾AQS的共享式释放同步状态的实现原理。

四、Extra Knowledge

4.1、CountDownLatch 和 CyclicBarrier的区别?

CountDownLatch和CyclicBarrier实现的效果看似都是某个线程等待一组线程达到条件之后才可继续执行,但是实际上两者存在很多区别。

1、CountDownLatch阻塞的是调用await()的线程,不会阻塞达到条件的线程;CyclicBarrier阻塞的是达到同步屏障的所有线程

2、CountDownLatch采用倒数计数,定义数量之后,每当一个线程达到要求之后就减一;CyclicBarrier是正数计数,当数量达到定义的数量之后就打开同步屏障

3、CountDownLatch仅单次有效,不可重复使用;CyclicBarrir可以循环重复使用

4、CountDownLatch定义的数量和实际线程数无关,可以有一个线程执行多次countDown();CyclicBarrier定义的数量和实际线程数一致,必须由多个线程都达到要求执行才行(线程调用await()方法之后就会被阻塞,想调用多次也不行的)

5、CountDownLatch是通过内部类Sync继承AQS来实现的;CyclicBarrier是通过重入锁ReentrantLock来实现的

6、CountDownLatch不可重置;CyclicBarrier可以调用reset方法进行重置

Java并发包5--同步工具CountDownLatch、CyclicBarrier、Semaphore的实现原理解析的更多相关文章

  1. 并发包下常见的同步工具类详解(CountDownLatch,CyclicBarrier,Semaphore)

    目录 1. 前言 2. 闭锁CountDownLatch 2.1 CountDownLatch功能简介 2.2 使用CountDownLatch 2.3 CountDownLatch原理浅析 3.循环 ...

  2. 并发包下常见的同步工具类(CountDownLatch,CyclicBarrier,Semaphore)

    在实际开发中,碰上CPU密集且执行时间非常耗时的任务,通常我们会选择将该任务进行分割,以多线程方式同时执行若干个子任务,等这些子任务都执行完后再将所得的结果进行合并.这正是著名的map-reduce思 ...

  3. JAVA多线程提高十:同步工具CyclicBarrier与CountDownLatch

    今天继续学习其它的同步工具:CyclicBarrier与CountDownLatch 一.CyclicBarrier CyclicBarrier是一个同步辅助类,它允许一组线程互相等待,直到到达某个公 ...

  4. Java并发编程工具类 CountDownLatch CyclicBarrier Semaphore使用Demo

    Java并发编程工具类 CountDownLatch CyclicBarrier Semaphore使用Demo CountDownLatch countDownLatch这个类使一个线程等待其他线程 ...

  5. Java核心知识点学习----线程同步工具类,CyclicBarrier学习

    线程同步工具类,CyclicBarrier日常开发较少涉及,这里只举一个例子,以做备注.N个人一块出去玩,相约去两个地方,CyclicBarrier的主要作用是等待所有人都汇合了,才往下一站出发. 1 ...

  6. Java并发包——线程同步和锁

    Java并发包——线程同步和锁 摘要:本文主要学习了Java并发包里有关线程同步的类和锁的一些相关概念. 部分内容来自以下博客: https://www.cnblogs.com/dolphin0520 ...

  7. CountDownLatch/CyclicBarrier/Semaphore 使用过吗?

    CountDownLatch/CyclicBarrier/Semaphore 使用过吗?下面详细介绍用法: 一,(等待多线程完成的)CountDownLatch  背景; countDownLatch ...

  8. JAVA多线程学习十三 - 同步工具CyclicBarrier与CountDownLatch

    一.CyclicBarrier CyclicBarrier是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point).在涉及一组固定大小的线程的程序 ...

  9. Java并发和多线程4:使用通用同步工具CountDownLatch实现线程等待

    CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待. 用给定的计数 初始化 CountDownLatch.由于调用了 countDown ...

随机推荐

  1. [Qt] 文本文件读写, 摘自官方文档

    Reading Files Directly The following example reads a text file line by line: QFile file("in.txt ...

  2. ansible一键安装mysql8.0

    ansbile安装: # ansible在CentOS7中需要安装epel仓库 yum install -y epel-release yum install -y ansible 安装有好几种方法, ...

  3. element-ui 通用表单封装及VUE JSX应用

    一.存在及需要解决的问题 一般在做后台OA的时候会发现表单重复代码比较多,且逻辑基本一样,每次新加一个表单都需要拷贝基本一致的代码结构,然后只是简单地修改对应的字段进行开发 二.预期结果 提取重复的表 ...

  4. 国际站中国区,孟买上Redis 4.0 集群版

    信息摘要: 国际站中国区,孟买上线Redis 4.0 集群版适用客户: 所有用户版本/规格功能: redis 4.0 集群版产品文档: https://www.alibabacloud.com/hel ...

  5. 信息竞赛进阶指南--Tire树

    // 假设字符串由小写字母构成 int trie[SIZE][26], tot = 1; // Trie的插入 void insert(char* str) { int len = strlen(st ...

  6. unittest 管理接口用例(数据分离-读取excel)

    1.公共模块 ---> login.xls """ common (package) ---> ReadFile.py """ ...

  7. 4)drf序列化组件 Serializer(偏底层)、ModelSerializer(重点)、ListModelSerializer(辅助群改)

    知识点:Serializer(偏底层).ModelSerializer(重点).ListModelSerializer(辅助群改) 一.Serializer 偏底层 一般不用 理解原理 1.序列化准备 ...

  8. Android控件重叠显示小记

    方案一 利用布局控件显示优先级 在xml中RelativeLayout,FrameLayout,靠后的控件显示在上层. 利用margin属性 margin属性可以控制控件间的距离,属性值为正值时,越大 ...

  9. 题目分享L

    题意:n个人围成一个环,每个人初始有一些金币,每个人可以把金币递给相邻的人,问最少传递多少金币使每个人金币数相同? 分析:首先在保证最优的情况下不可能会出现相邻的两个人互相送金币,因为这样他们公共的部 ...

  10. UTC 时间转化为北京时间

    // UTC 时间转化为北京时间 function utc2beijing(utcTime) { var T_pos = utcTime.indexOf('T'); var Z_pos = utcTi ...