基于AQS的前世今生,来学习并发工具类CountDownLatch。本文将从CountDownLatch的应用场景、源码原理解析来学习这个并发工具类。

1、 应用场景

  CountDownLatch是并发包中用来控制一个或者多个线程等待其他线程完成操作的并发工具类。现以工作中的一个场景来描述下CountDownLatch的应用,代码如下:

/*
模拟工作中的一个需求场景:
用户会选择多个算法来计算费用,最后会将所有算法计算出的费用做一个加权求平均数,这个平均数是最终的费用。
每个算法的复杂度都不一样,打算每个线程负责一个算法的实现,所有的线程执行完成,最后再求平均数。
1、为每个算法创建一个线程,每个线程负责一个算法的实现
2、通过CountDownLatch来控制所有算法线程的同步
3、全部计算完成后再求平均数
*/
public class CountDownLatchTask { public static void main(String[] args) {
CountDownLatchTask countDownLatchTask = new CountDownLatchTask();
countDownLatchTask.startThreads(5);
}
//根据线程数和选择的算法 调度算法对应的实现
private void startThreads(int threadNumber) {
CountDownLatch countDownLatch = new CountDownLatch(threadNumber);
for (int i = 0; i < threadNumber; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程算法实现:" + Thread.currentThread().getName());
countDownLatch.countDown();
}
}).start();
}
try {
countDownLatch.await();
System.out.println("加权求平均数");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

  在分析原理实现前,总结下CountDownLatch的作用就是阻塞其他线程直到条件允许后才释放该阻塞,除了上述这个小案例,实际工作中还有很多可以使用CountDownLatch的场景,比如解析Excel文件时可以同时解析多个Sheet页,所有的Sheet解析完成才算完成了Excel文件的解析。从这个代码中也可以看到CountDownLatch的主要方法就是await和countDown,下面将以这两个方法来分析下CountDownLatch的原理实现。

2、 源码原理解析

 2.1 await方法

  调用await方法会阻塞当前线程直到计数器的数值为0,方法如下:

public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1); //共享式获取AQS的同步状态
}

  调用的是AQS的acquireSharedInterruptibly方法:

public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())//线程中断 说明闭锁对线程中断敏感
throw new InterruptedException();
if (tryAcquireShared(arg) < 0) //闭锁未使用完成 线程进入同步队列自旋等待
doAcquireSharedInterruptibly(arg);
}

  其中tryAcquireShared依赖的是Sync的实现,和之前的ReentrantLockReentrantReadWriteLockSemaphore相比,CountDownLatch的Sync只提供了一种方式,代码如下:

protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1; //AQS的同步状态为0则闭锁结束 可以进行下一步操作
}

  doAcquireSharedInterruptibly方法就不再赘述,和之前Semaphore的实现是一致的,本质上仍然是AQS同步队列的入队自旋等待。

2.2 countDown方法

  调用countDown方法会将计数器的数值减1直到计数器为0,方法如下:

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

  和Semaphore一样,调用的是AQS的releaseShared方法:

public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {//减少闭锁的计数器
doReleaseShared();//唤醒后续线程节点
return true;
}
return false;
}

  其中tryReleaseShared依赖的是Sync的实现,和之前的ReentrantLockReentrantReadWriteLockSemaphore相比,CountDownLatch的Sync只提供了一种方式,代码如下:

protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false; //计数器已经是0了
int nextc = c-1; //计数器减1
if (compareAndSetState(c, nextc)) //CAS更新同步状态
return nextc == 0;
}
}

  唤醒后续线程节点的doReleaseShared也不再赘述,和之前Semaphore的实现是一致的。

  总结:CountDownLatch类使用AQS同步状态来表示计数。在await时,所有的线程进入同步队列自旋等待,在countDown时,获取闭锁成功的线程会减少闭锁的计数器,同时唤醒后续线程取获取闭锁,直到await中的计数器为0,获取到闭锁的线程才可以通过,执行下一步操作。

参考资料:

https://github.com/lingjiango/ConcurrentProgramPractice

Java并发编程-CountDownLatch的更多相关文章

  1. java并发编程CountDownLatch

    /** * CountDownLatch用法 * CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能.比如有一个任务A, * 它要等待其他4 ...

  2. 【Java并发编程实战】-----“J.U.C”:CountDownlatch

    上篇博文([Java并发编程实战]-----"J.U.C":CyclicBarrier)LZ介绍了CyclicBarrier.CyclicBarrier所描述的是"允许一 ...

  3. Java并发编程:CountDownLatch、CyclicBarrier和Semaphore

    Java并发编程:CountDownLatch.CyclicBarrier和Semaphore 在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch ...

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

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

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

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

  6. Java并发编程:CountDownLatch、CyclicBarrier和Semaphore (总结)

    下面对上面说的三个辅助类进行一个总结: 1)CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同: CountDownLatch一般用于某个线程A等待 ...

  7. 14、Java并发编程:CountDownLatch、CyclicBarrier和Semaphore

    Java并发编程:CountDownLatch.CyclicBarrier和Semaphore 在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch ...

  8. 【转】Java并发编程:CountDownLatch、CyclicBarrier和Semaphore

    Java并发编程:CountDownLatch.CyclicBarrier和Semaphore   Java并发编程:CountDownLatch.CyclicBarrier和Semaphore 在j ...

  9. java并发编程JUC第九篇:CountDownLatch线程同步

    在之前的文章中已经为大家介绍了java并发编程的工具:BlockingQueue接口.ArrayBlockingQueue.DelayQueue.LinkedBlockingQueue.Priorit ...

随机推荐

  1. Python爬虫学习之正则表达式爬取个人博客

    实例需求:运用python语言爬取http://www.eastmountyxz.com/个人博客的基本信息,包括网页标题,网页所有图片的url,网页文章的url.标题以及摘要. 实例环境:pytho ...

  2. C++ 知识回顾总结 -- 指针

    指针是一种特殊类型的变量,用于存储值的地址,因此,指针名表示的是地址.*运算符被成为间接值或解除引用运算符,将其应用于指针,可以得到该地址存储的值. 对指针的理解可以参照以下的例子: #include ...

  3. MySQL InnoDB 索引组织表 & 主键作用

    InnoDB 索引组织表 一.索引组织表定义 在InnoDB存储引擎中,表都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表(index organized table IOT). 在Inno ...

  4. 第83节:Java中的学生管理系统分页功能

    第83节:Java中的学生管理系统分页功能 分页功能一般可以做成两种,一种是物理分页,另一种是逻辑分页.这两种功能是有各自的特点的,物理分页是查询的时候,对数据库进行访问,只是查一页数据就进行返回,其 ...

  5. mysql数据统计技巧备忘录

    mysql 作为常用数据库,操作贼六是必须的,对于数字操作相关的东西,那是相当方便,本节就来拎几个统计案例出来供参考! order订单表,样例如下: CREATE TABLE `t_order` ( ...

  6. 一份从0到1的java项目实践清单

    虽说工作就是简单的事情重复做,但不是所有简单的事你都能有机会做的. 我们平日工作里,大部分时候都是在做修修补补的工作,而这也是非常重要的.做好修补工作,做好优化工作,足够让你升职加薪! 但是如果有机会 ...

  7. SpringBoot+logback实现日志打印

    logback介绍 logback是一款开源的日志框架,内核重写了,是基于log4j基础进行改良的.其官网为logback.qos.ch.logback在性能上有很大提升,拥有更多特性. logbac ...

  8. JVM读书笔记之OOM

    在Java虚拟机规范的描述中,除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError(OOM)异常的可能,本文总结了若干实例来验证异常及发生的场景. 下文代码的开头都 ...

  9. for循环输出素数探究【java】

    一.判断953是不是为素数(质数). 代码: /** 判断953是不是为素数(质数) 分析: 素数指整数在一个大于1的自然数中,除了1和此整数自身外,没法被其他自然数整除的数. 假设953是素数,则: ...

  10. DWR第四篇之对象传参

    1. 本示例在第一篇架构基础上添加代码 2. 首先,在dwr.xml文件里添加对象转换器 3. 编写Person实体类 package com.skyer.vo; import java.util.A ...