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状态,除非满足下面两个条件:

  1. count到0
  2. 线程中断
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的更多相关文章

  1. 并发编程JUC系列AQS(CountDownLatch、CyclicBarrier、Semaphore)

    一.CountDownLatch package com.jonychen.test; import java.util.concurrent.CountDownLatch; import java. ...

  2. 1.3.4 并发工具类CountDownLatch/Semaphore/CyclicBarrier/FutureTask

    CountDownLatch的2个用途: 1. 所有线程都到达相同的起跑线后,再一起开始跑(并非同时开始,而是队列中一个唤醒另一个)[此情况需到达起跑线后再调用await()等待其他线程] 2. 所有 ...

  3. 【Java多线程】JUC包下的工具类CountDownLatch、CyclicBarrier和Semaphore

    前言 JUC中为了满足在并发编程中不同的需求,提供了几个工具类供我们使用,分别是CountDownLatch.CyclicBarrier和Semaphore,其原理都是使用了AQS来实现,下面分别进行 ...

  4. CountDownLatch,CyclicBarrier,Semaphore

    CountDownLatch是倒数,doneSignal = new CountDownLatch(LATCH_SIZE);赋初值后,在主线程中等待doneSignal.await();其它线程中,每 ...

  5. 并发编程(七)——AbstractQueuedSynchronizer 之 CountDownLatch、CyclicBarrier、Semaphore 源码分析

    这篇,我们的关注点是 AQS 最后的部分,共享模式的使用.本文先用 CountDownLatch 将共享模式说清楚,然后顺着把其他 AQS 相关的类 CyclicBarrier.Semaphore 的 ...

  6. Java并发编程的4个同步辅助类(CountDownLatch、CyclicBarrier、Semaphore、Phaser)

    我在<JDK1.5引入的concurrent包>中,曾经介绍过CountDownLatch.CyclicBarrier两个类,还给出了CountDownLatch的演示案例.这里再系统总结 ...

  7. CountDownLatch、CyclicBarrier和Semaphore 使用示例及原理

    备注:博客园的markDown格式支持的特别不友好.也欢迎查看我的csdn的此篇文章链接:CountDownLatch.CyclicBarrier和Semaphore 使用示例及原理 CountDow ...

  8. JUC常用同步工具类——CountDownLatch,CyclicBarrier,Semaphore

    在 JUC 下包含了一些常用的同步工具类,今天就来详细介绍一下,CountDownLatch,CyclicBarrier,Semaphore 的使用方法以及它们之间的区别. 一.CountDownLa ...

  9. Java并发包5--同步工具CountDownLatch、CyclicBarrier、Semaphore的实现原理解析

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

  10. 温故知新-多线程-forkjoin、CountDownLatch、CyclicBarrier、Semaphore用法

    Posted by 微博@Yangsc_o 原创文章,版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0 文章目录 摘要 forkjoin C ...

随机推荐

  1. 《深入浅出RxJS》读书笔记

    rxjs的引入 // 如果以这种方式导入rxjs,那么整个库都会导入,我们一般不可能在项目中运用到rxjs的所有功能 const Rx = require('rxjs'); 解决这个问题,可以使用深链 ...

  2. C++ 洛谷 P2657 [SCOI2009]windy数 题解

    P2657 [SCOI2009]windy数 同步数位DP 这题还是很简单的啦(差点没做出来 个位打表大佬请离开(包括记搜),我这里讲的是DP!!! 首先Cal(b+1)-Cal(a),大家都懂吧(算 ...

  3. HDU 1286:找新朋友(欧拉函数)

    http://acm.hdu.edu.cn/showproblem.php?pid=1286 题意:中文. 思路:求欧拉函数. #include <cstdio> #include < ...

  4. 牛逼了,教你用九种语言在JVM上输出HelloWorld

    我们在<深入分析Java的编译原理>中提到过,为了让Java语言具有良好的跨平台能力,Java独具匠心的提供了一种可以在所有平台上都能使用的一种中间代码——字节码(ByteCode). 有 ...

  5. maven多仓库配置(公司服务器与阿里云仓库)

    1. 问题描述 公司内网搭建的有maven私有服务器,但是碰到好几次只有gav没有jar的情况或者最新版本更新不及时,所以需要公司服务器和远程仓库(阿里云)相结合来满足项目需求. 2. 解决方案: m ...

  6. ES6中的解构

    数组中的解构: 输出 : 白板 幺鸡 二条 对象的解构: 输出: 老王 12 数组的结构用[];对象的解构用{}:一定要区分它是数组还是解构. 区分方法:看 它是在赋值还是在拿值,等号左边,都为解构, ...

  7. 如何进行高效的源码阅读:以Spring Cache扩展为例带你搞清楚

    摘要 日常开发中,需要用到各种各样的框架来实现API.系统的构建.作为程序员,除了会使用框架还必须要了解框架工作的原理.这样可以便于我们排查问题,和自定义的扩展.那么如何去学习框架呢.通常我们通过阅读 ...

  8. Hadoop现在怎么样了?

    之前我们提到大数据的时候就会提到Hadoop,Hadoop是大数据的基础框架,是大数据技术的代表.提到HDFS.MapReduce.Yarn,提到HBase.Hive.TEZ等Hadoop生态圈中的一 ...

  9. HDU5521 Meeting(dijkstra+巧妙建图)

    HDU5521 Meeting 题意: 给你n个点,它们组成了m个团,第i个团内有si个点,且每个团内的点互相之间距离为ti,问如果同时从点1和点n出发,最短耗时多少相遇 很明显题目给出的是个无负环的 ...

  10. Zeppelin 0.6.2使用Spark的yarn-client模式

    Zeppelin版本0.6.2 1. Export SPARK_HOME In conf/zeppelin-env.sh, export SPARK_HOME environment variable ...