CountDownLatch

  1. A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

通常情况下,countDown如下调用

  1. CountDownLatch countDownLatch = new CountDownLatch(1);
  2. countDownLatch.countDown();
  3. countDownLatch.await();

看一下countDown方法:

  1. public void countDown() {
  2. sync.releaseShared(1);
  3. }

AQS中releaseShared方法如下:

  1. public final boolean releaseShared(int arg) {
  2. if (tryReleaseShared(arg)) {
  3. doReleaseShared();
  4. return true;
  5. }
  6. return false;
  7. }

CountDownLatch中tryReleaseShared方法如下:

  1. // 方法判断许可如果减1之后是否为0,如果为0的话就执行doReleaseShared()方法。
  2. protected boolean tryReleaseShared(int releases) {
  3. // Decrement count; signal when transition to zero
  4. for (;;) {
  5. int c = getState();
  6. if (c == 0)
  7. return false;
  8. int nextc = c-1;
  9. if (compareAndSetState(c, nextc))
  10. return nextc == 0;
  11. }
  12. }

来看doReleaseShared()方法:

  1. private void doReleaseShared() {
  2. /*
  3. * Ensure that a release propagates, even if there are other
  4. * in-progress acquires/releases. This proceeds in the usual
  5. * way of trying to unparkSuccessor of head if it needs
  6. * signal. But if it does not, status is set to PROPAGATE to
  7. * ensure that upon release, propagation continues.
  8. * Additionally, we must loop in case a new node is added
  9. * while we are doing this. Also, unlike other uses of
  10. * unparkSuccessor, we need to know if CAS to reset status
  11. * fails, if so rechecking.
  12. */
  13. for (;;) {
  14. Node h = head;
  15. if (h != null && h != tail) {
  16. int ws = h.waitStatus;
  17. if (ws == Node.SIGNAL) {
  18. if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
  19. continue; // loop to recheck cases
  20. unparkSuccessor(h);
  21. }
  22. else if (ws == 0 &&
  23. !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
  24. continue; // loop on failed CAS
  25. }
  26. if (h == head) // loop if head changed
  27. break;
  28. }
  29. }

不过尴尬的是,CountDownLatch这里未做任何事情。

再看一下await()方法:

await方法会让当前线程进入wait状态,除非满足下面两个条件:

  1. count到0
  2. 线程中断
  1. public void await() throws InterruptedException {
  2. sync.acquireSharedInterruptibly(1);
  3. }
  1. public final void acquireSharedInterruptibly(int arg)
  2. throws InterruptedException {
  3. if (Thread.interrupted())
  4. throw new InterruptedException();
  5. if (tryAcquireShared(arg) < 0)
  6. doAcquireSharedInterruptibly(arg);
  7. }

tryAcquireShared方法如下:

  1. protected int tryAcquireShared(int acquires) {
  2. return (getState() == 0) ? 1 : -1;
  3. }

所以,当state不是0的时候进入doAcquireSharedInterruptibly方法。

  1. private void doAcquireSharedInterruptibly(int arg)
  2. throws InterruptedException {
  3. final Node node = addWaiter(Node.SHARED);
  4. boolean failed = true;
  5. try {
  6. for (;;) {
  7. final Node p = node.predecessor();
  8. if (p == head) {
  9. // 只有当state为0时r为1
  10. int r = tryAcquireShared(arg);
  11. if (r >= 0) {
  12. setHeadAndPropagate(node, r);
  13. p.next = null; // help GC
  14. failed = false;
  15. return;
  16. }
  17. }
  18. // 如果state不为0,该线程会进入wait状态
  19. if (shouldParkAfterFailedAcquire(p, node) &&
  20. parkAndCheckInterrupt())
  21. throw new InterruptedException();
  22. }
  23. } finally {
  24. if (failed)
  25. cancelAcquire(node);
  26. }
  27. }

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项目,写一个方法:

  1. @RequestMapping("/test/semaphore")
  2. @ResponseBody
  3. public void test() throws InterruptedException {
  4. Semaphore semaphore = new Semaphore(5);
  5. for (int i = 0; i < 7; i++) {
  6. int finalI = i;
  7. new Thread(()->{
  8. try {
  9. semaphore.acquire();
  10. System.err.println(Thread.currentThread() + "获取了许可" + semaphore.availablePermits());
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. }, "线程" + i).start();
  15. }
  16. new Thread(()->{
  17. try {
  18. Thread.sleep(10000);
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. System.err.println(Thread.currentThread() + "要释放许可" + semaphore.availablePermits());
  23. semaphore.release();
  24. }, "线程7").start();
  25. }

一次输出如下:

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()方法如下:

  1. public int await() throws InterruptedException, BrokenBarrierException {
  2. try {
  3. return dowait(false, 0L);
  4. } catch (TimeoutException toe) {
  5. throw new Error(toe); // cannot happen
  6. }
  7. }

进入dowait方法:

  1. private int dowait(boolean timed, long nanos)
  2. throws InterruptedException, BrokenBarrierException,
  3. TimeoutException {
  4. final ReentrantLock lock = this.lock;
  5. lock.lock();
  6. try {
  7. final Generation g = generation;
  8. if (g.broken)
  9. throw new BrokenBarrierException();
  10. if (Thread.interrupted()) {
  11. breakBarrier();
  12. throw new InterruptedException();
  13. }
  14. // 来一个线程count减1,如果index为0,就会翻车
  15. int index = --count;
  16. if (index == 0) { // tripped
  17. boolean ranAction = false;
  18. try {
  19. final Runnable command = barrierCommand;
  20. if (command != null)
  21. command.run();
  22. ranAction = true;
  23. nextGeneration();
  24. return 0;
  25. } finally {
  26. if (!ranAction)
  27. breakBarrier();
  28. }
  29. }
  30. // 没翻车(broken,interrupted,timed out)的话就执行下面的逻辑
  31. // loop until tripped, broken, interrupted, or timed out
  32. for (;;) {
  33. try {
  34. if (!timed)
  35. trip.await();
  36. else if (nanos > 0L)
  37. nanos = trip.awaitNanos(nanos);
  38. } catch (InterruptedException ie) {
  39. if (g == generation && ! g.broken) {
  40. breakBarrier();
  41. throw ie;
  42. } else {
  43. // We're about to finish waiting even if we had not
  44. // been interrupted, so this interrupt is deemed to
  45. // "belong" to subsequent execution.
  46. Thread.currentThread().interrupt();
  47. }
  48. }
  49. if (g.broken)
  50. throw new BrokenBarrierException();
  51. if (g != generation)
  52. return index;
  53. if (timed && nanos <= 0L) {
  54. breakBarrier();
  55. throw new TimeoutException();
  56. }
  57. }
  58. } finally {
  59. lock.unlock();
  60. }
  61. }

下面进入trip.await()方法

  1. public final void await() throws InterruptedException {
  2. if (Thread.interrupted())
  3. throw new InterruptedException();
  4. // 往等待队列加入节点Node
  5. Node node = addConditionWaiter();
  6. // 这里释放AQS中的state, 如果释放失败,会将node的waitstatus置为CANCELLED,这是传参node的唯一用处
  7. int savedState = fullyRelease(node);
  8. int interruptMode = 0;
  9. // 如果node有next就肯定返回true
  10. while (!isOnSyncQueue(node)) {
  11. LockSupport.park(this);
  12. if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
  13. break;
  14. }
  15. // 如果当前线程
  16. if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
  17. interruptMode = REINTERRUPT;
  18. if (node.nextWaiter != null) // clean up if cancelled
  19. unlinkCancelledWaiters();
  20. if (interruptMode != 0)
  21. reportInterruptAfterWait(interruptMode);
  22. }

进入addConditionWaiter()

  1. private Node addConditionWaiter() {
  2. Node t = lastWaiter;
  3. // If lastWaiter is cancelled, clean out.
  4. if (t != null && t.waitStatus != Node.CONDITION) {
  5. unlinkCancelledWaiters();
  6. t = lastWaiter;
  7. }
  8. Node node = new Node(Thread.currentThread(), Node.CONDITION);
  9. if (t == null)
  10. firstWaiter = node;
  11. else
  12. t.nextWaiter = node;
  13. lastWaiter = node;
  14. return node;
  15. }

假如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. MySQL metalock的一些技巧(写大于读的案例,以及获得锁的顺序)

    前言:元数据锁不是锁定数据,而是锁定描述数据的元数据信息.就像很多装修工人(工作线程)在室内(对象上)装修(操作),不能有其他工人(线程)把屋子拆了(表删除了). MySQL 为了数据一致性使用元数据 ...

  2. HDU 3065:病毒侵袭持续中(AC自动机)

    http://acm.hdu.edu.cn/showproblem.php?pid=3065 题意:中文题意. 思路:直接插入然后用一个数组记录id和cnt,因为n只有1000,可以开一个数组判断第几 ...

  3. 【RabbitMQ】一文带你搞定RabbitMQ死信队列

    本文口味:爆炒鱿鱼   预计阅读:15分钟 一.说明 RabbitMQ是流行的开源消息队列系统,使用erlang语言开发,由于其社区活跃度高,维护更新较快,性能稳定,深得很多企业的欢心(当然,也包括我 ...

  4. React躬行记(7)——表单

    表单元素是一类拥有内部状态的元素,这些状态由其自身维护,通过这类元素可让用户与Web应用进行交互.HTML中的表单元素(例如<input>.<select>和<radio ...

  5. Spring Cloud Alibaba | Nacos配置管理

    目录 Spring Cloud Alibaba | Nacos配置管理 1. pom.xml 项目依赖 2. 在 bootstrap.properties 中配置 Nacos server 的地址和应 ...

  6. spring系列(一):超级经典入门

    一  spring是什么 Spring是一个开源框架,它由RodJohnson创建.它是为了解决企业应用开发的复杂性而创建的.Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情. ...

  7. solidity智能合约中tx.origin的正确使用场景

    简介 tx.origin是Solidity的一个全局变量,它遍历整个调用栈并返回最初发送调用(或事务)的帐户的地址.在智能合约中使用此变量进行身份验证会使合约容易受到类似网络钓鱼的攻击. 但针对tx. ...

  8. Netty使用Google Protocol Buffer完成服务器高性能数据传输

    一.什么是Google Protocol Buffer(protobuf官方网站) 下面是官网给的解释: Protocol buffers are a language-neutral, platfo ...

  9. Ansible配置文件ansible.cfg详解

    Ansible是一个系列文章,我会尽量以通俗易懂.诙谐幽默的总结方式给大家呈现这些枯燥的知识点,让学习变的有趣一些. Ansible系列博文直达链接:Ansible入门系列 前言 此时外面小雨淅淅沥沥 ...

  10. [leetcode] 106. Construct Binary Tree from Inorder and Postorder Traversal(medium)

    原题地址 思路: 和leetcode105题差不多,这道题是给中序和后序,求出二叉树. 解法一: 思路和105题差不多,只是pos是从后往前遍历,生成树顺序也是先右后左. class Solution ...