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 ...
随机推荐
- java虚拟机-GC-调优
1. 年轻代大小选择 * 响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择).在此种情况下,年轻代收集发生的频率也是最小的.同时,减少到达年老代的对象. * 吞吐量优先 ...
- java-NIO-DatagramChannel(UDP)
Java NIO中的DatagramChannel是一个能收发UDP包的通道.因为UDP是无连接的网络协议,所以不能像其它通道那样读取和写入.它发送和接收的是数据包. 打开 DatagramChann ...
- 并发编程-concurrent指南-阻塞队列-同步队列SynchronousQueue
SynchronousQueue:同步Queue,属于线程安全的BlockingQueue的一种,此队列设计的理念类似于"单工模式",对于每个put/offer操作,必须等待一个t ...
- SpringBoot 的过滤器
在Springboot里面读封装的一些常用的API,当然对过滤器也不类外了. 首先讲下Spring中的AOP的理解: AOP不是一种具体的技术,而是一种编程思想.在面向对象编程的过程中,我们很容易通过 ...
- 那些有实力进入 BAT 的本科生,都做对了什么事?
作者:黄小斜 文章来源:微信公众号[黄小斜] 最近这段时间,我们部门来了几个年纪轻轻的本科生,最小的比我们小五岁左,这对于我来说还是比较有冲击力的. 想想我也是九0出头的老腊肉了,想当年我上大学的时候 ...
- django项目中遇到要实现定时任务
django项目中遇到要实现定时任务,所以选用了简单易用的django-crontab插件. 1.安装 django-crontab pip install django-crontab 2.定时要执 ...
- Zimg—轻量级图片服务器搭建利器
在一个互联网应用中,图片扮演着越来越重要的角色.有稳定的可扩展的图片存储服务器就显得尤为的重要,云厂商们提供了便利的图片存储服务,花钱就可以解决了.这里简单介绍一个开源的一个分布式图片存储服务器--z ...
- 基于C#的机器学习--微基准测试和激活功能
本章我们将学习以下内容: l 什么是微基准测试 l 如何将它应用到代码中 l 什么是激活函数 l 如何绘制和基准测试激活函数 每个开发人员都需要有一个好的基准测试工具.质量基准无处不在;你们每 ...
- HTML连载23-属性选择器(上)
一.属性选择器 1. (1)定义:根据指定的 属性名称找到对应的标签,然后设置属性 (2)格式:标签[属性=值]:{属性:值:] 注意:前一个值是不带引号的 (3)例子: <style> ...
- Scala 函数式编程(一) 什么是函数式编程?
为什么我们需要学习函数式编程?或者说函数式编程有什么优势?这个系列中我会用 scala 给你讲述函数式编程中的优势,以及一些函数式的哲学.不懂 scala 也没关系,scala 和 java 是类似的 ...