CountDownLatch简介

  CountDownLatch是通过“共享锁”实现的。在创建CountDownLatch中时,会传递一个int类型参数count,该参数是“锁计数器”的初始状态,表示该“共享锁”最多能被count给线程同时获取。当某线程调用该CountDownLatch对象的await()方法时,该线程会等待“共享锁”可用时,才能获取“共享锁”进而继续运行。而“共享锁”可用的条件,就是“锁计数器”的值为0!而“锁计数器”的初始值为count,每当一个线程调用该CountDownLatch对象的countDown()方法时,才将“锁计数器”-1;通过这种方式,必须有count个线程调用countDown()之后,“锁计数器”才为0,而前面提到的等待线程才能继续运行!

CountDownLatch和CyclicBarrier的区别
(01) CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待。
(02) CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。

CountDownLatch函数列表

CountDownLatch(int count)
构造一个用给定计数初始化的 CountDownLatch。 // 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
void await()
// 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。
boolean await(long timeout, TimeUnit unit)
// 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。
void countDown()
// 返回当前计数。
long getCount()
// 返回标识此锁存器及其状态的字符串。
String toString()

学习CountDownLatch的核心函数

1.CountDownLatch(int count)

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

说明:该函数是创建一个Sync对象,而Sync是继承于AQS类。Sync构造函数如下:

Sync(int count) {
setState(count);
}

setState()在AQS中实现,源码如下:

protected final void setState(long newState) {
state = newState;
}

说明:在AQS中,state是一个private volatile long类型的对象。对于CountDownLatch而言,state表示的”锁计数器“。CountDownLatch中的getCount()最终是调用AQS中的getState(),返回的state对象,即”锁计数器“。

2.await()

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

说明:该函数实际上是调用的AQS的acquireSharedInterruptibly(1);

AQS中的acquireSharedInterruptibly()的源码如下:

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

说明:acquireSharedInterruptibly()的作用是获取共享锁。
如果当前线程是中断状态,则抛出异常InterruptedException。否则,调用tryAcquireShared(arg)尝试获取共享锁;尝试成功则返回,否则就调用doAcquireSharedInterruptibly()。doAcquireSharedInterruptibly()会使当前线程一直等待,直到当前线程获取到共享锁(或被中断)才返回。

tryAcquireShared()在CountDownLatch.java中被重写,它的源码如下:

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

说明:tryAcquireShared()的作用是尝试获取共享锁。
如果"锁计数器=0",即锁是可获取状态,则返回1;否则,锁是不可获取状态,则返回-1。

3.countDown()

  

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

说明:该函数实际上调用releaseShared(1)释放共享锁。

releaseShared()在AQS中实现,源码如下:

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

说明:releaseShared()的目的是让当前线程释放它所持有的共享锁。
它首先会通过tryReleaseShared()去尝试释放共享锁。尝试成功,则直接返回;尝试失败,则通过doReleaseShared()去释放共享锁。

tryReleaseShared()在CountDownLatch.java中被重写,源码如下:

protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
// 获取“锁计数器”的状态
int c = getState();
if (c == 0)
return false;
// “锁计数器”-1
int nextc = c-1;
// 通过CAS函数进行赋值。
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}

说明:tryReleaseShared()的作用是释放共享锁,将“锁计数器”的值-1。

CountDownLatch实例

public class testHello {

    private static int LATCH_SIZE = 5;
private static CountDownLatch doneSignal;
public static void main(String[] args) { try {
doneSignal = new CountDownLatch(LATCH_SIZE); // 新建5个任务
for(int i=0; i<LATCH_SIZE; i++)
new InnerThread().start(); System.out.println("main await begin.");
// "主线程"等待线程池中5个任务的完成
doneSignal.await(); System.out.println("main await finished.");
} catch (InterruptedException e) {
e.printStackTrace();
}
} static class InnerThread extends Thread{
public void run() {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " sleep 1000ms.");
// 将CountDownLatch的数值减1
doneSignal.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

  可以看出,主线程通过doneSignal.await()等待其他线程将doneSignal递减至0,其他的几个子线程,每次通过countDown函数将doneSignal值减1。当doneSignal为0时,main被唤醒继续执行。

多线程编程-- part 7 CountDownLatch的更多相关文章

  1. java核心-多线程-Java多线程编程涉及到包、类

    Java有关多线程编程设计的类主要涉及两个包java.lang和java.util.concurrent两个包 java.lang包,主要是线程基础类 <1>Thread <2> ...

  2. [Java123] JDBC and Multi-Threading 多线程编程学习笔记

    项目实际需求:DB交互使用多线程实现 多线程编程基础:1.5  :( (假设总分10) 计划一个半月从头学习梳理Java多线程编程基础以及Oracle数据库交互相关的多线程实现 学习如何通过代码去验证 ...

  3. Java多线程编程实战指南(核心篇)读书笔记(三)

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76686044冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...

  4. Java多线程编程(5)--线程间通信

    一.等待与通知   某些情况下,程序要执行的操作需要满足一定的条件(下文统一将其称之为保护条件)才能执行.在单线程编程中,我们可以使用轮询的方式来实现,即频繁地判断是否满足保护条件,若不满足则继续判断 ...

  5. 《Java多线程编程实战指南(核心篇)》阅读笔记

    <Java多线程编程实战指南(核心篇)>阅读笔记 */--> <Java多线程编程实战指南(核心篇)>阅读笔记 Table of Contents 1. 线程概念 1.1 ...

  6. Web Worker javascript多线程编程(一)

    什么是Web Worker? web worker 是运行在后台的 JavaScript,不占用浏览器自身线程,独立于其他脚本,可以提高应用的总体性能,并且提升用户体验. 一般来说Javascript ...

  7. Web Worker javascript多线程编程(二)

    Web Worker javascript多线程编程(一)中提到有两种Web Worker:专用线程dedicated web worker,以及共享线程shared web worker.不过主要讲 ...

  8. windows多线程编程实现 简单(1)

    内容:实现win32下的最基本多线程编程 使用函数: #CreateThread# 创建线程 HANDLE WINAPI CreateThread( LPSECURITY_ATTRIBUTES lpT ...

  9. Rust语言的多线程编程

    我写这篇短文的时候,正值Rust1.0发布不久,严格来说这是一门兼具C语言的执行效率和Java的开发效率的强大语言,它的所有权机制竟然让你无法写出线程不安全的代码,它是一门可以用来写操作系统的系统级语 ...

随机推荐

  1. js evenloop

    一.宏任务 vs 微任务 1.macrotask setTimeOut . setInterval . setImmediate . I/O . 各种callback.UI渲染等 优先级: 主代码块 ...

  2. AMBARI部署HADOOP集群(4)

    通过 Ambari 部署 hadoop 集群 1. 打开 http://192.168.242.181:8080  登陆的用户名/密码是 : admin/admin 2. 点击 “LAUNCH INS ...

  3. NAT地址转换常用命令详解

    缺省值:没有启用NAT. 命令模式:全局配置模式. 说明:静态NAT主要用于那些对需要对外部用户开放的服务,如Web服务器等,它可以把本地地址映射为指定的全局地址. 第一种格式实现的是一对一的NAT映 ...

  4. Linux 服务器基本优化

    一:修改ulimit数 vi /etc/security/limits.conf 添加如下行: * soft noproc 65535 * hard noproc 65535 * soft nofil ...

  5. java源码-HashMap类设计

    map(内部interface Entry<K,V>)->abstractMap(定义视图 entrySet抽象方法)->hashMap(静态内部类Node(继承Entry&l ...

  6. 【转】Linux curl命令详解

    [From]https://www.cnblogs.com/duhuo/p/5695256.html 命令:curl 在Linux中curl是一个利用URL规则在命令行下工作的文件传输工具,可以说是一 ...

  7. .net core中读取配置文件

    1)先看丑陋的方法 读取 appsettings.json   然后在 Startup 的 ConfigureServices() 方法中进行注入: public IConfigurationRoot ...

  8. GO-REDIS的一些高级用法

    1. 前言 说到Golang的Redis库,用到最多的恐怕是redigo 和 go-redis.其中 redigo 不支持对集群的访问.本文想聊聊go-redis 2个高级用法 2. 开启对Clust ...

  9. Linux C/C++基础——变量作用域

    1.局部变量 局部变量也叫auto自动变量(auto可写可不写),一般情况下代码块{}内部定义的变量都是自动变量,它有如下特点: 只有当执行到这句语句时,系统才为这个变量分配空间 在一个函数内定义,只 ...

  10. 记录Linq中lambda动态表达式的使用方式

    项目中有的时候我们会用到动态表达式的方式去查询数据,这里简单记录下个人的使用方式,方便使用↓ //构建参数表达式 ParameterExpression parameter = Expression. ...