AQS之CountDownLatch、Semaphore、CyclicBarrier
CountDownLatch
A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
通常情况下,countDown如下调用
CountDownLatch countDownLatch = new CountDownLatch(1);
countDownLatch.countDown();
countDownLatch.await();
看一下countDown方法:
public void countDown() {
sync.releaseShared(1);
}
AQS中releaseShared方法如下:
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
CountDownLatch中tryReleaseShared方法如下:
// 方法判断许可如果减1之后是否为0,如果为0的话就执行doReleaseShared()方法。
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
来看doReleaseShared()方法:
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
不过尴尬的是,CountDownLatch这里未做任何事情。
再看一下await()方法:
await方法会让当前线程进入wait状态,除非满足下面两个条件:
- count到0
- 线程中断
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
tryAcquireShared方法如下:
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
所以,当state不是0的时候进入doAcquireSharedInterruptibly方法。
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
// 只有当state为0时r为1
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
// 如果state不为0,该线程会进入wait状态
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
CountDownLatch文档中有一句非常重要的话:
Memory consistency effects: Until the count reaches zero, actions in a thread prior to calling countDown()
happen-before actions following a successful return from a corresponding await()
in another thread
大意是一个线程countdown()之前的操作happens-before另一个线程中await()之后的操作。
Semaphore
Semaphores are often used to restrict the number of threads than can access some (physical or logical) resource.
Semaphore主要用来限制获取资源的线程数。
Actions in a thread prior to calling a "release" method such as release() happen-before actions following a successful "acquire" method such as acquire() in another thread
内存语义:release() happen-before acquire()之前
启一个springboot项目,写一个方法:
@RequestMapping("/test/semaphore")
@ResponseBody
public void test() throws InterruptedException {
Semaphore semaphore = new Semaphore(5);
for (int i = 0; i < 7; i++) {
int finalI = i;
new Thread(()->{
try {
semaphore.acquire();
System.err.println(Thread.currentThread() + "获取了许可" + semaphore.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "线程" + i).start();
}
new Thread(()->{
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.err.println(Thread.currentThread() + "要释放许可" + semaphore.availablePermits());
semaphore.release();
}, "线程7").start();
}
一次输出如下:
Thread[线程1,5,main]获取了许可4
Thread[线程0,5,main]获取了许可3
Thread[线程3,5,main]获取了许可2
Thread[线程4,5,main]获取了许可0
Thread[线程2,5,main]获取了许可0
Thread[线程7,5,main]要释放许可0
Thread[线程5,5,main]获取了许可0
会发现,线程5获取许可之前是先等线程7释放许可。
至于线程6会因为由于许可为0,进入等待状态。直到有线程释放许可,来调用unparkSuccessor。
CyclicBarrier
A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.
Actions in a thread prior to calling await() happen-before actions that are part of the barrier action, which in turn happen-before actions following a successful return from the corresponding await() in other threads.
内部类Generation只有一个属性broken(默认false)
我们发现,await()方法如下:
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
进入dowait方法:
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
// 来一个线程count减1,如果index为0,就会翻车
int index = --count;
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// 没翻车(broken,interrupted,timed out)的话就执行下面的逻辑
// loop until tripped, broken, interrupted, or timed out
for (;;) {
try {
if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
}
}
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
下面进入trip.await()方法
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 往等待队列加入节点Node
Node node = addConditionWaiter();
// 这里释放AQS中的state, 如果释放失败,会将node的waitstatus置为CANCELLED,这是传参node的唯一用处
int savedState = fullyRelease(node);
int interruptMode = 0;
// 如果node有next就肯定返回true
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 如果当前线程
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
进入addConditionWaiter()
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
假如5个线程按顺序进入await(),则此时,trip这个ConditionObject上firstWaiterlastWaiternew Node("线程0对应的线程", Node.CONDITION)
同时,因为dowait方法中的lock.lock(),AQS的同步队列如下:
head节点--》线程1--》线程2--》线程3--》线程4(tail)
等待队列: t0
当释放线程0的锁之后,唤醒线程1,将线程1加入等待队列,线程2/3也加入等待队列。此时同步队列还剩下线程4。此时队列情况是:
同步队列:head节点
等待队列:t0->t1->t2->t3
到了最后一个线程4执行的时候,index==0,执行nextGeneration,会signalAll trip这个Condition上的所有等待线程。所以经过signalAll之后,队列情况变成了:
同步队列:head->t0->t1->t2->t3
等待队列:空
此时线程4运行,释放锁之后唤醒同步队列上的第一个节点t0
AQS之CountDownLatch、Semaphore、CyclicBarrier的更多相关文章
- 并发编程JUC系列AQS(CountDownLatch、CyclicBarrier、Semaphore)
一.CountDownLatch package com.jonychen.test; import java.util.concurrent.CountDownLatch; import java. ...
- 1.3.4 并发工具类CountDownLatch/Semaphore/CyclicBarrier/FutureTask
CountDownLatch的2个用途: 1. 所有线程都到达相同的起跑线后,再一起开始跑(并非同时开始,而是队列中一个唤醒另一个)[此情况需到达起跑线后再调用await()等待其他线程] 2. 所有 ...
- 【Java多线程】JUC包下的工具类CountDownLatch、CyclicBarrier和Semaphore
前言 JUC中为了满足在并发编程中不同的需求,提供了几个工具类供我们使用,分别是CountDownLatch.CyclicBarrier和Semaphore,其原理都是使用了AQS来实现,下面分别进行 ...
- CountDownLatch,CyclicBarrier,Semaphore
CountDownLatch是倒数,doneSignal = new CountDownLatch(LATCH_SIZE);赋初值后,在主线程中等待doneSignal.await();其它线程中,每 ...
- 并发编程(七)——AbstractQueuedSynchronizer 之 CountDownLatch、CyclicBarrier、Semaphore 源码分析
这篇,我们的关注点是 AQS 最后的部分,共享模式的使用.本文先用 CountDownLatch 将共享模式说清楚,然后顺着把其他 AQS 相关的类 CyclicBarrier.Semaphore 的 ...
- Java并发编程的4个同步辅助类(CountDownLatch、CyclicBarrier、Semaphore、Phaser)
我在<JDK1.5引入的concurrent包>中,曾经介绍过CountDownLatch.CyclicBarrier两个类,还给出了CountDownLatch的演示案例.这里再系统总结 ...
- CountDownLatch、CyclicBarrier和Semaphore 使用示例及原理
备注:博客园的markDown格式支持的特别不友好.也欢迎查看我的csdn的此篇文章链接:CountDownLatch.CyclicBarrier和Semaphore 使用示例及原理 CountDow ...
- JUC常用同步工具类——CountDownLatch,CyclicBarrier,Semaphore
在 JUC 下包含了一些常用的同步工具类,今天就来详细介绍一下,CountDownLatch,CyclicBarrier,Semaphore 的使用方法以及它们之间的区别. 一.CountDownLa ...
- Java并发包5--同步工具CountDownLatch、CyclicBarrier、Semaphore的实现原理解析
前言: JUC中提供了很多同步工具类,比如CountDownLatch.CyclicBarrier.Semaphore等,都可以作用同步手段来实现多线程之间的同步效果 一.CountDownLatch ...
- 温故知新-多线程-forkjoin、CountDownLatch、CyclicBarrier、Semaphore用法
Posted by 微博@Yangsc_o 原创文章,版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0 文章目录 摘要 forkjoin C ...
随机推荐
- 性能测试即服务-docker部署jmeter及.netcore应用
前言 现在各种业务都追求上云,通俗的讲,“XX即服务”,作为一名专职的性能测试调优人员的我,由于会点三脚猫的开发功夫,“性能测试即服务”这种开发大任就落到我头上了,先做一个能完成核心压测功能的基础版. ...
- [常用命令]Git命令
取得Git仓库 初始化一个版本仓库 git init Clone远程版本库 git clone https://github.com/yhj167/yhj167.github.io.git 添加远程版 ...
- Linux命令学习-cp命令
Linux中,cp命令的全称是copy,主要作用是复制文件或文件夹,类似于Windows下的复制功能. 假设当前处于wintest用户的主目录,路径为 /home/wintest ,存在文件夹test ...
- Android 装逼技术之暗码启动应用
什么是暗码? 在拨号盘中输入*#*#<code>#*#*后,APP 可以监控到这些输入,然后做相应的动作,比如启动应用,是不是有点骚. 下面看下这个骚操作是如何实现的. 效果预览 源码 D ...
- 『开发技巧』Keras自定义对象(层、评价函数与损失)
1.自定义层 对于简单.无状态的自定义操作,你也许可以通过 layers.core.Lambda 层来实现.但是对于那些包含了可训练权重的自定义层,你应该自己实现这种层. 这是一个 Keras2.0 ...
- ServiceFabric极简文档-5.1 编程模型选择
项目中:actor用的服务是无状态服务:ASP.NET Core用的是无状态ASP.NET Core模板.
- 附录:1-Grain生命周期-译注
Grain Lifecycle Grains are logical entities that always exist, virtually, and have stable logical id ...
- DataGrid通过DataSet保存为xml文件,并导入
做了个小的DataGrid通过DataSet保存为xml_测试,DataGrid通过DataSet保存为xml_测试,通过dataSet.writeXML()和dataSet.readXML()方法完 ...
- 微信小程序 textarea 层级过高的解决方式
建立一个新的textarea 组件代替原生textarea ,废话不多说,上代码 <template> <view class="ui-textarea"> ...
- [leetcode] 929. Unique Email Addresses (easy)
统计有几种邮箱地址. 邮箱名类型:local@domain 规则:1. local中出现"."的,忽略. a.bc=abc 2. local中出现"+"的,+以 ...