当前位置: 首页 > news >正文

JUC CountDownLatch源码详解

基础用法

  • 初始化一个 计数器(count)。
  • 线程调用 await()阻塞,直到计数器变成 0。同时还可以支持带超时时间的await()。
  • 其他线程可以调用 countDown() 让计数器减 1。
  • 当计数器变成 0 时,所有因 await() 阻塞的线程会被同时唤醒。

流程如下:

public static void main(String[] args) throws InterruptedException {CountDownLatch countDownLatch = new CountDownLatch(3);new Thread(() -> {try {Thread.sleep(10);System.out.println(Thread.currentThread().getName() + "执行");} catch (InterruptedException e) {throw new RuntimeException(e);}finally {countDownLatch.countDown();}}, "t1").start();new Thread(() -> {try {Thread.sleep(10);System.out.println(Thread.currentThread().getName() + "执行");} catch (InterruptedException e) {throw new RuntimeException(e);}finally {countDownLatch.countDown();}}, "t2").start();new Thread(() -> {try {Thread.sleep(10);System.out.println(Thread.currentThread().getName() + "执行");} catch (InterruptedException e) {throw new RuntimeException(e);}finally {countDownLatch.countDown();}}, "t3").start();Thread.currentThread().setName("main");countDownLatch.await();System.out.println(Thread.currentThread().getName() + "执行");}

详细说明:

  • 首先new了一个CountDownLatch,计数器初始化为3
  • 接下来新建三个线程执行任务,每个任务各自结束时执行countDown。此时主主线程调用await(),处于阻塞状态
  • 三个线程任务执行完毕,count == 0,主线程停止阻塞执行任务

源码详解

属性解释

想要了解CountDownLatch,首先要了解AQS是什么。接下来对其进行简要说明。

AQS(AbstractQeueudSynchronizer)抽象队列同步器,是CLH锁的改进。是由Node组成的双向队列。

可以看到,Node内部属性主要由等待线程、前驱节点,后继节点,以及status构成。前驱节点与后继节点无需过多说明,主要来关注status代表什么,有什么作用。

status有三种状态:

WAITTING:为1。节点已经被唤醒,正在主队列中等待获取锁

COND:为2。表示节点正在条件队列中等待,等待某个条件成立

CANCELLED:为负数。表示取消,遍历时快速跳过

可以看到,CountDownLatch的同步控制就是通过Sync类实现的,Sync又继承了AQS。

初始化

private final Sync sync;
public CountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException("count < 0");this.sync = new Sync(count);}
    Sync(int count) {setState(count);}

初始化CountDownLatch时需要传入计数,然后借此计数new一个Sync出来。Sync的构造函数表明就是将AQS队列的状态设为count。

await()

public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);}

先调用了AQS的acquireSharedInterruptibly方法,这是AQS提供的共享模式并且可中断的获取资源的方法。可以冲进去看一眼。

protected int tryAcquireShared(int acquires) {return (getState() == 0) ? 1 : -1;//判断计数器代表的state是否为0,即要等待执行完的线程已经全部执行完}
public final void acquireSharedInterruptibly(int arg)throws InterruptedException {if (Thread.interrupted() ||(tryAcquireShared(arg) < 0 &&acquire(null, arg, true, true, false, 0L) < 0))throw new InterruptedException();}

可以看到,他会进行条件判断,如果线程的状态是可以打断的,则直接抛异常。

tryAcquireShared则表示判断查询锁的状态是否允许在共享模式下获取它(计数器为0时),如果不能获取,则调用acquire来获取锁资源或者入队。如果acquire方法也无法获取资源或者入队,则抛异常

acquire方法简单解释:

  1. 判断当前节点是否为头节点
  2. 如果是头节点或者还没有入队,则直接尝试获取锁资源
  3. 获取锁资源失败,将直接入队,阻塞线程直到被唤醒或中断

经过tryAcquireShared和acquire两轮成功判断,调用latch.await()的线程要么直接获取到了锁资源(tryAcquireShared -> state = 0),要么进入了阻塞队列(tryAcquireShared -> state > 0 && acquire != 0 )。

countDown()

调用的是AQS的releaseShared(int arg)方法,如果成功释放锁资源,则唤醒下一个节点。

protected boolean tryReleaseShared(int releases) {for (;;) {int c = getState();if (c == 0)return false;//计数器中代表的线程 已经全部执行,再释放就返回falseint nextc = c - 1;//计数器 -1if (compareAndSetState(c, nextc))return nextc == 0;//通过CAS,判断当前线程是否为最后一个释放的线程}}
public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {//如果是最后一个释放的线程,则直接唤醒调用await()的资源signalNext(head);return true;}return false;}

从源码可以看到,每有一个线程调用countDown,就会通过CAS将计数器-1直至0。

当减到0的时候,会进行判断并唤醒阻塞队列中的线程。

因为只有一个线程调用了await()方法,所以唤醒的线程必定是此线程。然后调用latch.await()的线程恢复正常执行。

总结流程

我将用上文所举例的代码总结流程。

这是执行结果。

首先初始化计数器为3,即status = 3。新建三个线程分别执行各自的方法,执行完毕时调用countDown()。

主线程进入await()方法,如果打断标记为true,则直接抛异常。然后调用tryAcquireShared()发现status如果为零,则直接执行主线程的方法。如果不为0,因此调用acquire尝试入队。如果入队失败,直接抛异常。否则当前线程入队成功。

新建的三个线程分别异步执行。当t2执行结束后判断status == 0,然后就调用signal方法将阻塞队列中唯一的main线程唤醒,从而调用main方法。

这就是CountDownLatch的全部执行流程。

http://www.dtcms.com/a/409739.html

相关文章:

  • Unity-动画IK控制
  • 华天软件Inforcenter PLM uploadFileTolls接口存在任意文件上传漏洞
  • FT2000/4核Linux下GPIO中断调试说明
  • 从后端到react框架
  • 几个Linux系统安装体验: 银河麒麟服务器系统V11
  • 金仓数据库打通电子证照国产化“最后一公里”——福建某地2TB MongoDB无缝迁移实践
  • 宁波网站建设明细报价上海红蚂蚁装潢设计有限公司
  • 罗田企业网站建设在贸易网站怎么做贸易
  • 前端登录加密实战:从原理到落地,守护用户密码安全
  • 公司网站域名申请流程江苏营销型网站建设
  • 做外单网站亚马逊如何做ps4的游戏视频网站
  • 企业门户网站建设特色小程序营销策划方案
  • 网站加ico图标如何登陆网站空间
  • 外贸网站建设 东莞企业信息管理系统登录
  • 天津品牌网站建设推广产品吸引人的句子
  • 网站建设价格标准渠道电商设备网站怎么做
  • 秦皇岛网站制作费用服务器维护费用明细
  • 浙江鼎兴建设有限公司网站加强政协机关网站建设
  • 做视频网站 带宽多少才合适谷歌优化推广
  • 网站设计费用入哪个会计科目宜都市网站建设
  • 建标网seo是指什么职位
  • i深圳网站建设wordpress数据库排序规则
  • 服务器做视频网站江苏省宝应城市建设有限公司网站
  • 网站默认图片北京市建设工程第四检测所网站
  • 安网站建设免费一键logo在线设计
  • 数码产品销售网站建设策划书做外贸需要具备什么条件
  • 空间备案网站cms 网站
  • 欧美网站设计风格深圳家具定制
  • 如何通过建立网站赚钱龙岗南联网站建设
  • win2003做网站软文营销什么意思