前言

前面四节学完了AQS最难的两种重入锁应用,下面两节进入实战学习,看看JUC包中其他的工具类是如何运用AQS实现特定功能的。今天一起看一下CountDownLatch。

CountDownLatch可以用来实现多个线程执行完一个功能后让另一个线程继续执行的功能。常见的场景比如大文件的处理,我们需要对一个或多个文件进行处理,处理完之后再统一入库,这时我们就可以用到CountDownLatch了。

一、使用样例

 public static void main(String[] args) {
// 指定初始容量
CountDownLatch latch = new CountDownLatch(3);
// 启动三个线程,每个线程独自处理文件
for (int i = 0;i < 3; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 正在处理文件");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 处理完毕");
latch.countDown();
}).start();
}
try {
latch.await();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("所有文件处理完成后,统一入库");
}

运行结果:

 Thread-0 正在处理文件
Thread-2 正在处理文件
Thread-1 正在处理文件
Thread-0 处理完毕
Thread-2 处理完毕
Thread-1 处理完毕
所有文件处理完成后,统一入库

效果就是这样,下面我们一起看看它是如何实现的这种功能。

二、源码学习

1、首先我们看看new CountDownLatch(3) 做了什么事情

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

继续追踪可以发现,就是将count赋值给AQS中的成员变量state,表示已经有3个线程占用了锁。

2、看countDown()方法做了什么

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

可以看到,countDown走的是释放共享锁的逻辑,从给state赋值也可以猜到用的是共享锁-有多个线程且state可赋大于0的值。继续看releaseShared逻辑:

 public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}

可以看到就是读锁释放的逻辑,其中doReleaseShared方法实现逻辑相同就不看了,不同的是tryReleaseShared方法,下面跟进:

 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;
}
}

此方法在CountDownLatch中的内部类Sync中得到实现,逻辑为将state-1,并且如果是0的话返回true。返回true后在releaseShared方法中会进入if里面,走唤醒后续节点的逻辑doReleaseShared方法,在该方法中唤醒的main线程。main线程什么时候被挂起的?且看下面。

3、await方法

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

await调用了可响应中断的获取共享锁方法,继续查看:

 public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}

此方法是AQS中的公用模板方法,不同点在于各实现类的实现逻辑,在CountDownLatch中对tryAcquireShared方法进行了实现,实现逻辑如下:

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

即如果state==0则能获取到锁,否则获取不到。获取不到进入下面的doAcquireSharedInterruptibly方法,最终会将head的waitStatus设置为-1,自己挂起等待唤醒。

三、总结

CountDownLatch是基于共享锁实现的并发控制功能,现在对总的实现逻辑做个梳理:首先在构造器初始化CountDownLatch的时候,就会给AQS中的state赋值,表示共享锁已经被获取了N次;然后每执行一次countDown则共享锁释放一次,直到释放完;await方法是加锁的逻辑,但加锁条件是state==0时才会加锁成功,否则挂起;最后,当通过countDown的调用将state减为0后,会唤醒处于阻塞状态的主线程,让其 获取到锁并执行。

AQS系列(五)- CountDownLatch的使用及原理的更多相关文章

  1. java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析

    java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...

  2. java多线程系列(八)---CountDownLatch和CyclicBarrie

    CountDownLatch 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 java多线 ...

  3. AQS系列(七)- 终篇:AQS总结

    前言 本文是对之前AQS系列文章的一个小结,首先看看以下几个问题: 1.ReentrantLock和ReentrantReadWriteLock的可重入特性是如何实现的? 2.哪个变量控制着锁是否被占 ...

  4. java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别

    java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别 目录 java基础解析系列(一)---String.StringBuffer.St ...

  5. AQS系列(一)- ReentrantLock的加锁

    前言 AQS即AbstractQueuedSynchronizer,是JUC包中的一个核心抽象类,JUC包中的绝大多数功能都是直接或间接通过它来实现的.本文是AQS系列的第一篇,后面会持续更新多篇,争 ...

  6. AQS系列(三)- ReentrantReadWriteLock读写锁的加锁

    前言 前两篇我们讲述了ReentrantLock的加锁释放锁过程,相对而言比较简单,本篇进入深水区,看看ReentrantReadWriteLock-读写锁的加锁过程是如何实现的,继续拜读老Lea凌厉 ...

  7. Java并发编程系列-(8) JMM和底层实现原理

    8. JMM和底层实现原理 8.1 线程间的通信与同步 线程之间的通信 线程的通信是指线程之间以何种机制来交换信息.在编程中,线程之间的通信机制有两种,共享内存和消息传递. 在共享内存的并发模型里,线 ...

  8. 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念

    深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.AQS框架简介 AQS诞生于Jdk1.5,在当时低效且功能单一的synchroni ...

  9. 深入理解Java并发框架AQS系列(四):共享锁(Shared Lock)

    深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 深入理解Java并发框架AQS系列(三):独占锁(Exclusive Lock) 深入 ...

  10. CSS 魔法系列:纯 CSS 绘制各种图形《系列五》

    我们的网页因为 CSS 而呈现千变万化的风格.这一看似简单的样式语言在使用中非常灵活,只要你发挥创意就能实现很多比人想象不到的效果.特别是随着 CSS3 的广泛使用,更多新奇的 CSS 作品涌现出来. ...

随机推荐

  1. IDEA用Maven连接MySQL的jdbc驱动,并操作数据库

    1.在IDEA里创建Maven项目 1.1.点击Create New Project   1.2.选择Maven,JDK这里用的是1.8,点击Next  1.3.填入“组织名”.“项目名”,版本是默认 ...

  2. Mysql操作之查询语句

    查询语句: select 查询列表 from 表名 where 筛选条件; 去重:select distinct 查询列表...... 选择全部:* 起别名:select 查询列表 as 别名 fro ...

  3. day 28 网络基础相关的知识

    1.网络基础相关的知识 架构 C/S 架构:  client 客户端  server服务器端 优势: 能充分发挥PC机的性能 B/S 架构: browser 浏览器 server服务器       隶 ...

  4. 自定制页面跳转时携带原搜索参数的URL

    介绍 django自带反向解析生成URL的功能,目的是避免硬编码,较少代码维护的代价. 前端页面使用模板语法,如:{% url "rbac: request menu_list" ...

  5. sqlserver查询(子查询,全连接,等值连接,自然连接,左右连,交集,并集,差集)

    --部门表 create table dept( deptno int primary key,--部门编号 dname ),--部门名 loc )--地址 ); --雇员表 create table ...

  6. CCNA 之 十三 广域网概述

    广域网概述 为什么需要WAN ? 分区或分支机构的员工需要与总部通信并共享数据: 组织经常需要与其他组织远距离共享信息: 经常出差的员工需要访问公司网络信息: 什么事广域网链路? 用于连接LAN的.跨 ...

  7. c语言l博客作业08

    问题 答案 这个作业属于那个课程 C语言程序设计II 这个作业要求在哪里 https://edu.cnblogs.com/campus/zswxy/CST2019-2/homework/8655 我在 ...

  8. c 语言I博客作业02

    这个作业属于哪个课程 C语言程序设计1 这个作业要求在哪里 https://edu.cnblogs.com/campus/zswxy/SE2019-2/homework/8687 我在这个课程的目标是 ...

  9. css实现对勾

    <!DOCTYPE html><html> <head> <meta charset=%;background-color: #2ac845;} %;back ...

  10. 英飞凌TC297 PSPR与DSPR

    TC297的系统架构图如下,3个核CPU0/1/2有各自的RAM,分为程序RAM和数据RAM,即PSPR和DSPR.PSPR(或者叫做PRAM)主要用于放置静态函数,提高函数执行效率,或放置flash ...