CyclicBarrier、CountDownLatch以及Semaphore使用及其原理分析

CyclicBarrier、CountDownLatch以及Semaphore是Java并发包中几个常用的并发组件,这几个组件特点是功能相识很容易混淆。首先我们分别介绍这几个组件的功能然后再通过实例分析和源码分析其中设计原理。

CyclicBarrier

主要功能:

CyclicBarrier的主要功能是使1~(N-1)个线程达到某个屏障后阻塞,直到第N个线程到达该屏障后才会被打开,这是所有的线程才能继续执行下去,CyclicBarrier同时支持一个Runable对象,当所有线程到达该屏障时执行该Runable对象。

用例:

输出:

Thread-0达到屏障 Thread-2达到屏障 Thread-1达到屏障 Thread-1离开屏障 Thread-2离开屏障 Thread-0离开屏障

可以看到三个线程同时达到屏障后所有线程才开始离开屏障继续运行。下面我们将分析其设计原理。

设计原理

CyclicBarrier调用await()方法是线程等待,await()方法源码如下:

其内部调用的是doWait()方法,await()还有一个带超时的重载方法,功能类似。doWait()方法代码如下:

Generation是一个静态内部类,表明CyclicBarrier的代,表明每个CyclicBarrier执行的实例,如果当前CyclicBarrier正常执行完将会重置代,否则将会破坏代。

breakBarrier方法会破坏屏障,可以看到起设置了代为破坏状态同时调用Condition的signalAll方法唤醒所有在等待的线程。

nextGeneration主要作用为重置下一代,内部也会唤醒正在等待的线程同时将屏障数量复位方便下一次使用。

CountDownLatch

CountDownLatch的主要功能是实现几个计数器,使N个现场执行完成后当前线程才会继续执行下去。比如我们希望将一个事件分成多个线程去执行,执行完后进行汇总这种情景就可以使用CountDownLatch。

用例

输出:

Thread-0执行完毕 Thread-1执行完毕 Thread-2执行完毕 Thread-3执行完毕 Thread-4执行完毕 主线程执行

原理分析

构造方法

CountDownLatch构造函数中调用了Sync构造方法,Sync继承了AQS内容如下:

设置了state,这个东西再熟悉不过了,在可重入锁中表示是否获取到锁的标志位。

我们首先看await方法,

实际调用的是AQS的acquireSharedInterruptibly方法,从名字可以看出采用的是共享模式。

tryAcquireShared代码在Sync中被实现如下:

如果state等于0返回1,否则返回-1;state等于0说明没有对象在同步器中,线程可以继续执行下去,否则进入doAcquireSharedInterruptibly方法中,doAcquireSharedInterruptibly方法如下:

这段代码就是共享模式获取节点,获取不到就进入队列中休眠,这个跟读写锁一样,知道state等于0后被唤醒。

countDown方法如下:

调用的是Sync的releaseShared方法,

countDown方法的主要功能就是通过CAS方法减少state的值,减少成功后唤醒队列中的节点。唤醒主节点成功后doAcquireSharedInterruptibly中方法会继续执行接着判断state是否等于0,不等与继续休眠否则继续执行线程。

Semaphore

Semaphore可以控制访问线程的数量。

用例:

原理分析

首先看构造方法,

默认构造方法采用非公平模式。

acquire方法如下:

很熟悉默认采用的是共享模式获取节点信息,跟读锁类似。

release方法如下:

acquireSharedInterruptibly方法如下:

tryReleaseShared方法如下:

总结

CyclicBarrier主要用于N个线程之间互斥,当且仅当N个线程都执行到屏障处所有线程才能继续执行下去,CyclicBarrier可以被重复使用,CyclicBarrier通过可冲入锁+AQS+Condition实现,CyclicBarrier调用await方法获取可重入锁同时减少state的值,state==0时唤醒所有正在等待的线程,否则线程处于等待状态,线程间的通信主要通过Condition机制来实现。

CountDownLatch主要用于某个线程等待N个线程执行完后等待的线程接着继续执行下去,不能够重复执行,CountDownLatch通过设施AQS state值来实现,每次调用counDown方法后都去唤醒正在等待的线程,等待的线程判断state是否等于0,等于0就继续执行。

Semaphore用于控制访问线程的数量,Semaphore通过设置AQS state值来实现,调用require方法后cas减少state的值,如果state值为负数说明有更多线程正在访问代码块,这是后需要把这些线程休眠,调用release方法后重新增加state值,重新增加state值后去唤醒正在等待的线程。

原文发布于微信公众号 - IT技术精选文摘(ITHK01)

原文发表时间:2018-06-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏一个会写诗的程序员的博客

并发安全的 ConcurrentHashMap 实现原理详解并发安全的 ConcurrentHashMap 实现原理详解不变(Immutable)和易变(Volatile)定位段

哈希表是中非常高效,复杂度为O(1)的数据结构,在Java开发中,我们最常见到最频繁使用的就是HashMap和HashTable,但是在线程竞争激烈的并发场景中...

661
来自专栏微信公众号:Java团长

Java多线程并发编程一览笔录

方式一:将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法

1022
来自专栏java一日一条

从源码看concurrentHashMap的线程安全

从JDK1.2起,就有了HashMap,正如前一篇文章所说,HashMap不是线程安全的,因此多线程操作时需要格外小心。

1052
来自专栏搜云库

想进大厂?50个多线程面试题,你会多少?(一)

最近看到网上流传着,各种面试经验及面试题,往往都是一大堆技术题目贴上去,而没有答案。

1.4K7
来自专栏Java技术栈

史上最全Java多线程面试题及答案

多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域。所以,学好多线程并发编程对Java程序员来来说极其重要的。 下面小编整理了60道最常见的Ja...

42011
来自专栏老码农专栏

原 荐 ActFramework 发布 1.

2233
来自专栏于晓飞的专栏

Java 并发 学习笔记

最近重新复习了一边并发的知识,发现自己之前对于并发的了解只是皮毛。这里总结以下Java并发需要掌握的点。

1112
来自专栏Golang语言社区

gopath 设置问题

实际上GOPATH, 可以是一个目录的列表, 在windows环境变量里设置的时候, 多个目录使用分号(;)隔开, 在linux里多个目录使用冒号(隔开, 组合...

44511
来自专栏Danny的专栏

System.AccessViolationException”类型的未经处理的异常在 System.Data.dll 中发生。其他信息:尝试读取或写入受保护的内存。这通常指示其他内存已损坏。

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/...

1252
来自专栏龙首琴剑庐

Java多线程并发编程一览笔录

知识体系图: ? 1、线程是什么? 线程是进程中独立运行的子任务。 2、创建线程的方式 方式一:将类声明为 Thread 的子类。该子类应重写 Thread 类...

38010

扫码关注云+社区

领取腾讯云代金券