Java多线程系列——计数器 CountDownLatch
简介:
CountDownLatch 是一个非常实用的多线程控制工具类,通常用来控制线程的等待,它可以让某个线程等待直到倒计时结束
CountDownLatch 提供了两个主要的方法,await()、countDown()。
await:使当前线程阻塞,等待计数器为 0
countDown:计数器减一,计数为零时,释放所有在等待的线程
实例:
public class CountDownLatchDemo implements Runnable {
static final CountDownLatch end = new CountDownLatch(10);
static final CountDownLatchDemo demo = new CountDownLatchDemo();
@Override
public void run() {
try {
Thread.sleep(new Random().nextInt(10) * 1000);
System.out.println("check complete...");
end.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService exec = Executors.newFixedThreadPool(10);
for (int i = 0;i < 10;i++) {
exec.submit(demo);
}
end.await();
System.out.println("Fire!");
exec.shutdown();
}
}
原理解析:
countDownLatch 的计数是通过一个共享变量(volatile)实现的,下面分析它的三个核心函数:构造函数,CountDownLatch(int count);阻塞线程,await();计数器减一,countDown()。
CountDownLatch(int count)
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
其中 Sync 是 CountDownLatch 的内部类,并且 Sync 继承了 AbstractQueuedSynchronizer
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
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;
}
}
}
其中 setState 是设置 AbstractQueuedSynchronizer 中 state 变量,该变量声明了 volatile。
State 就是 countDownLatch 中的计数器。
await()
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquireShared(arg) >= 0 ||
doAcquireSharedNanos(arg, nanosTimeout);
}
acquireSharedInterruptibly() 的作用是获取共享锁,如果当前线程处于中断状态,则抛出 InterruptedException,否则,调用 tryAcquireShared(arg) 尝试获取共享锁,如果锁计数器 = 0,则表示锁为可获取状态,返回 1,否则,锁为不可获取状态,则返回 -1。
doAcquireSharedNanos() 会使当前线程一直等待,直到当前线程获取到共享锁(或线程被中断)才返回。
countDown()
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
releaseShared() 目的是让当前线程释放它所持有的共享锁。
tryReleaseShared() 的作用是释放共享锁,将锁计数器的值减一。
总结
CountDownLatch 是通过共享锁实现的。CountDownLatch 构造函数传递 int 参数,该参数是计数器的初始状态,表示共享锁最多能被 count 个线程同时获取。
当某线程调用 CountDownLatch 的 await 方法时,该线程会等待共享锁可用时(计数器为 0 时),才能获取共享锁,进而继续执行。
每次执行 countDown 时,会将计数器减一。
参考资料
Java多线程系列——计数器 CountDownLatch的更多相关文章
- java多线程系列(八)---CountDownLatch和CyclicBarrie
CountDownLatch 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 java多线 ...
- java多线程系列:CountDownLatch
这篇文章将介绍CountDownLatch这个同步工具类的基本信息以及通过案例来介绍如何使用这个工具. CountDownLatch是java.util.concurrent包下面的一个工具类,可以用 ...
- Java多线程系列——从菜鸟到入门
持续更新系列. 参考自Java多线程系列目录(共43篇).<Java并发编程实战>.<实战Java高并发程序设计>.<Java并发编程的艺术>. 基础 Java多线 ...
- java多线程系列(九)---ArrayBlockingQueue源码分析
java多线程系列(九)---ArrayBlockingQueue源码分析 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 j ...
- Java多线程系列--“JUC锁”09之 CountDownLatch原理和示例
概要 前面对"独占锁"和"共享锁"有了个大致的了解:本章,我们对CountDownLatch进行学习.和ReadWriteLock.ReadLock一样,Cou ...
- Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例
概要 本章介绍JUC包中的CyclicBarrier锁.内容包括:CyclicBarrier简介CyclicBarrier数据结构CyclicBarrier源码分析(基于JDK1.7.0_40)Cyc ...
- Java多线程系列--“JUC锁”01之 框架
本章,我们介绍锁的架构:后面的章节将会对它们逐个进行分析介绍.目录如下:01. Java多线程系列--“JUC锁”01之 框架02. Java多线程系列--“JUC锁”02之 互斥锁Reentrant ...
- Java多线程系列--“JUC锁”03之 公平锁(一)
概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...
- Java多线程系列--“JUC锁”04之 公平锁(二)
概要 前面一章,我们学习了“公平锁”获取锁的详细流程:这里,我们再来看看“公平锁”释放锁的过程.内容包括:参考代码释放公平锁(基于JDK1.7.0_40) “公平锁”的获取过程请参考“Java多线程系 ...
随机推荐
- VBA中FIND方法的使用说明zz
Find 方法在区域中查找特定信息. 语法 表达式.Find(What, After, LookIn, LookAt, SearchOrder, SearchDirection, MatchCase, ...
- IAR7.51提示秘钥无效IAR 以及 CCDebug驱动(包含win7 64bit)
今天IAR不识别我的仿真器,然后我感觉驱动有问题,就把之前的驱动卸载了,但是按照以前的方法按章驱动(选择路径到IAR的某个目录),提示找不到驱动... 也不想重新装个IAR了,于是到CSDN上下载了这 ...
- setdest 和cbrgen工具的使用,出现的错误
在路径 ~/ns-2.34/indep-utils/cmu-scen-gen/setdest下运行 ./setdest -n 250 -p 0.0 -M 10.0 -t 10 -x 1500 -y 1 ...
- node cluster模块,仿多线程并发调用,
worker.js var cluster = require('cluster')function fibo(n) { return n == 0 ? 0 : n > 1 ? fibo(n - ...
- System.out.println()和System.err.println()
在一次笔试中遇到了一个System.err.println()的输出,之前没有见过,回来查一查,自己还是见识太短,来补充一下. 首先看一看jdk中 来一个简单的实验 第一次显示 第二次显示 1. 发现 ...
- (转)JavaWeb学习之Servlet(三)----Servlet的映射匹配问题、线程安全问题
[声明] 欢迎转载,但请保留文章原始出处→_→ 文章来源:http://www.cnblogs.com/smyhvae/p/4140529.html 一.Servlet映射匹配问题: 在第一篇文章中的 ...
- PhantomJS、CasperJS安装配置图文详解
目前网站主流的加载方式: 一种是同步加载:另一种是异步加载,也即我们常说的用ajax.对于同步加载的网站,普通的爬虫程序轻松就能搞定.但是对于那种异步请求数据的网站,通常使用selenium+Phan ...
- Java类集 List, Set, Map, Stack, Properties基本使用
首先看下继承结构: ArrayList(常用): /** * List接口继承Collection接口 * ArrayList, Vector为List接口的实现类 * add()添加新元素,remo ...
- html-webpack-plugin插件使用chunks属性时报错
报错如下图: 原因是之前template属性使用的html模板内添加了script标签,将其删掉再使用chunks属性自动添加即可. (注释掉也不可以,必须删除!)
- ES6_入门(6)_函数的扩展
// 2017/7/22 /*ES6函数的扩展*/ //ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法. function log(x, y) { y = y || 'World'; ...