本文来自公众号“Kahuna”,可搜索Alitaba119,欢迎关注,转载请注明出处,非常感谢


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

这是CountDownLatch这个类要解决的问题:实现一个同步器,让一个或者多个线程一直等待,直到一组在其他线程中执行的操作完成。


目录

一、CountDownLatch类工作流程

二、CountDownLatch类用法

三、CountDownLatch类源码解析

四、CountDownLatch类的思考点

五、CountDownLatch类的适用场景

六、文章透过ReentrantLock窥探AQS问题回答

七、AQS系列推荐


一、CountDownLatch类用法

先看下我们的测试代码,waitThread1和waitThread2两个线程等待latch,另外5个任务线程执行到latch.countDown(),任务就绪(此时任务线程会继续执行后面的逻辑),等最后一个任务执行latch.countDown()之后waitThread1,waitThread2被唤醒继续执行。

下图是两次执行结果:


从上图结果中可以看出来,waitThread1和waitThread2两个线程等待5个任务线程就绪之后才继续执行;另外waitThread1和waitThread2的执行和5个任务线程的执行没有固定的先后顺序,任务线程执行完countDown()方法之后会继续执行后面的逻辑。

二、CountDownLatch类工作流程

通过下面这个动图来展示CountDownLatch的整个工作流程,需要注意的是等待线程和任务线程的区别,另外在任务就绪之后,整个释放的流程需要重点关注,共享节点的释放并不是固定一个个节点按顺序释放的,而是可能多个节点同时释放的。


三、CountDownLatch类源码解析

CountDownLatch源码相对来说还是比较简单的,最核心的就是理解他的工作步骤以及同步器的实现。
工作步骤:

  • 初始化的时候定义几个任务,即同步器中state的数量
  • 等待线程执行await方法等待state变成0,等待线程会进入同步器的等待队列
  • 任务线程执行countDown方法之后,state值减1,直到减到0,唤醒等待队列中所有的等待线程。

同步器实现了tryAcquireShared方法,判断当state!=0的时候把等待线程加入到等待队列并阻塞等待线程,state=0的时候这个latch就不能够再向等待队列添加等待线程;另外实现了tryReleaseShared,判断当前任务是否是最后一个任务,当state减到0的时候就是最后一个任务,然后会以传播唤醒的方式唤醒等待队列中的所有等待线程。


四、CountDownLatch类的思考点

这边的思考点只有一个,latch任务线程都就绪之后怎么释放?要了解这个点,我们需要具体看下AQS中的doReleaseShared方法。

不知道大家有没有想过,上图1,2,3处代码,作者为什么要这么写?

在解释这三处代码之前,我们首先要弄清楚SHARD节点的释放方式。

假如我们有以下的共享节点组成的AQS等待队列:

主线程调用doReleaseShared方法,进入for循环自旋,head节点的地址赋值给h,然后判断是否有后继节点并需要释放,最后判断h中的地址和当前的head节点的地址是否一样,如果一样,则退出for循环,流程图如下:

每一个被唤醒的线程都会进入自旋逻辑,而且每个线程都有一个head地址的本地变量副本,当这个副本和当前的head节点的地址不一致的时候,会继续自旋, 获取最新的head节点,去尝试唤醒下一个线程。我们看下下面的这几种时序:

第一处代码为什么需要把waitStatus设置成0?
如果waitStatus=NODE.SIGNAL,不把状态设置成0,则自旋的时候有可能重复触发唤醒同一个节点(上图中如果主线程,t1线程,t2线程自旋的时候,head指向线程t3节点,waitStatus如果等于-1,则线程t4就会被重复唤醒)。

第二处代码:为什么等于0的时候要设置NODE.PROPAGATE?
还是拿上面的case举例子,假如现在head指向线程t4,如果主线程,t1线程,t2线程,t3线程同时完成自己的唤醒任务,然后都获取了线程t4的head地址,第一个线程把线程t4节点的waitStatus改成了0,那后面的线程会尝试把0改成PROPAGATE(这里过来的线程有两种可能,修改waitStatus从-1到0失败继续自旋过来,或者是判断waitStatus=0过来),如果不成功,则自旋,如果成功则判断head是否有变化,简单的说就是让竞争修改waitStatus为PROPAGATE失败的线程继续释放其他的节点。

第三处代码:为什么要设置h==head退出自旋?

最主要的原因是,不然我怎么跳出自旋呢!另外如果head不一致,当前线程可以帮忙一起释放剩余的等待线程,提高释放效率。


五、CountDownLatch类的适用场景

如果你要执行一个任务,但是必须先等其他几个任务做到某个程度这个任务才能够启动,这个时候就比较适合用CountDownLatch。但是一般会使用超时等待的方式来处理,不然如果其中某个任务异常没有完成,或者超时了,那么任务会一直等待在那里。


六、文章透过ReentrantLock窥探AQS问题回答

目前ReentrantLock窥探AQS文章还没收集到问题,有兴趣的同学可以点进去看看,在文章下留言就行


七、AQS系列推荐

“ 本系列文章旨在对并发包中的AQS以及跟AQS相关的锁做一个深入的讲解,让各位看官可以深入理解AQS以及对应的锁”

透过ReentrantLock窥探AQS

透过CountDownLatch窥探AQS

AQS条件队列和同步队列的关系

透过ReentrantReadWriteLock窥探AQS

通过Semaphore窥探AQS


尾声


tips:文章中的部分图片是传到微博上再导过来的(微信文章中的不能直接用),有点糊,如果想要高清的图片可以关注我的公众号Alitaba119,公众号文章中的图片比较清楚。

如果本文章解决了各位看官心里的一些疑惑,可以关注本公众号,后续会有更多精彩文章,也可以帮忙推荐给其他的小伙伴,让更多的人受益,万分感谢。

如果看了文章之后心中还有疑惑,请在文章下面留言,下一篇文章「AQS条件队列和同步队列的关系」中我会对前一篇文章大家的问题做一个解答。

透过CountDownLatch窥探AQS的更多相关文章

  1. 透过ReentrantLock窥探AQS

    背景 JDK1.5引入的并发包提供了一系列支持中等并发的类,这些组件是一系列的同步器,几乎任一同步器都可以实现其他形式的同步器,例如,可以用可重入锁实现信号量或者用信号量实现可重入锁.但是,这样做带来 ...

  2. Java多线程之---用 CountDownLatch 说明 AQS 的实现原理

    本文基于 jdk 1.8 . CountDownLatch 的使用 前面的文章中说到了 volatile 以及用 volatile 来实现自旋锁,例如 java.util.concurrent.ato ...

  3. 透过 ReentrantLock 分析 AQS 的实现原理

    对于 Java 开发者来说,都会碰到多线程访问公共资源的情况,这时候,往往都是通过加锁来保证访问资源结果的正确性.在 java 中通常采用下面两种方式来解决加锁得问题: synchronized 关键 ...

  4. Java多线程系列--“JUC锁”09之 CountDownLatch原理和示例

    概要 前面对"独占锁"和"共享锁"有了个大致的了解:本章,我们对CountDownLatch进行学习.和ReadWriteLock.ReadLock一样,Cou ...

  5. JUC系列回顾之-CountDownLatch底层原理和示例

    CountDownLatch 是一个同步工具类,允许一个线程或者多个线程等待其他线程完成操作,再执行. CountDownLatch(int count) 构造一个用给定计数初始化的 CountDow ...

  6. 解读java同步类CountDownLatch

    同步辅助类: CountDownLatch是一个同步辅助类,在jdk5中引入,它允许一个或多个线程等待其他线程操作完成之后才执行. 实现原理 : CountDownLatch是通过计数器的方式来实现, ...

  7. 并发是个什么鬼之同步工具类CountDownLatch

    扯淡 写这篇文章,我先酝酿一下,实不相瞒,脱离底层太久了,更确切的情况是,真没曾认真研究过.就目前来说,很多框架包括工具类已经把实现封装的很深,你只需轻轻的调用一下API,便不费半点力气.以至于大家会 ...

  8. Java 线程同步组件 CountDownLatch 与 CyclicBarrier 原理分析

    1.简介 在分析完AbstractQueuedSynchronizer(以下简称 AQS)和ReentrantLock的原理后,本文将分析 java.util.concurrent 包下的两个线程同步 ...

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

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

随机推荐

  1. AWS 数据分析服务(十)

    Amazon Kinesis 概念 处理AWS上大量流数据的数据平台 Kinesis Streams 用于搜集数据,Client Library 用于分析后的展示 构建用于处理或分析流数据的自定义应用 ...

  2. solr关于日期范围查询

    问题:从solr上查询创建日期在2019-06-25到2019-06-26之间的数据 createDate:[2019-06-25 TO 2019-06-26]

  3. redis 那些事儿

    1 我的数据存入redis了但是怎么不见了? redis的内存使用是有限的,一直向redis中写入数据(如果配置了allkeyLRU)就会触发内存淘汰机制,将最近没有访问过的的key,value删除掉 ...

  4. [WinForm] - "更新 DataSet 应用程序集对象失败,Visual Studio 自动重启" 之解决

    背景 在 WinForm 解决方案中,更新 DataSet 应用程序集对象失败,Visual Studio 自动重启. 试一试 1. 更新 .xsd 时打开对应的 .Designer.cs.2. 如果 ...

  5. Asp.Net Core中创建多DbContext并迁移到数据库

    在我们的项目中我们有时候需要在我们的项目中创建DbContext,而且这些DbContext之间有明显的界限,比如系统中两个DbContext一个是和整个数据库的权限相关的内容而另外一个DbConte ...

  6. git config 介绍

    转载. https://blog.csdn.net/liuxiao723846/article/details/83113317 Git的三个重要配置文件分别是/etc/gitconfig,${HOM ...

  7. CentOS7+Docker+MangoDB下部署简单的MongoDB分片集群

    简单的在Docker上快速部署MongoDB分片集群 前言 文中使用的环境如下 OS:CentOS Linux release 7.5.1804 (Core) Docker:Docker versio ...

  8. Feign 客户端调用错误

    1.@RequestBody 必须要写在实现接口中 2.Feign 客户端调用的时候如果有参数的话,默认是发送post请求 3.服务接口中的请求参数必须要加上@RequestParam("r ...

  9. redis主从中断异常处理

    线上预警主从中断: 查看线上复制信息: # Replication role:slave master_host:master_host master_port:6379 master_link_st ...

  10. java 线程实现、线程暂停和终止 、线程联合join、线程基本信息获取和设置、线程优先级

    转载地址:速学堂 https://www.sxt.cn/Java_jQuery_in_action/eleven-inheritthread.html 1. 通过继承Thread类实现多线程 继承Th ...