Java多线程同步工具类之CountDownLatch
在过去我们实现多线程同步的代码中,往往使用join()、wait()、notiyAll()等线程间通信的方式,随着JUC包的不断的完善,java为我们提供了丰富同步工具类,官方也鼓励我们使用工具类来实现多线程的同步,今天我们就对其中CountDownLatch类的使用与底层实现进行分析与总结。
一、CountDownLatch使用
CountDownLatch其实可以看做一个计数器,统计多个线程执行完成的情况,适用于控制一个或多个线程等待,直到所有线程都执行完毕的场景,类似与Thread.join()的作用。下面我们通过一个简单的例子看下CountDownLatch的使用。
public static void main(String[] args) {
final CountDownLatch countDownLatch = new CountDownLatch(5);
// 启动计数线程
for (int i = 0; i < 5; i++) {
new CountDownLatchThread(i, countDownLatch).start();
}
// 启动等待线程
for (int i = 0; i < 5; i++) {
new Thread() {
public void run() {
try {
countDownLatch.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("计数完毕了," + Thread.currentThread().getName() + "等待线程执行");
}
}.start();
}
}
计数线程代码:
public class CountDownLatchThread extends Thread {
private CountDownLatch countDownLatch;
private int name;
private int count;
public CountDownLatchThread(int name, CountDownLatch countDownLatch) {
this.name = name;
this.countDownLatch = countDownLatch;
this.count = 0;
}
public void run() {
try {
for (int i = 0; i < 10; i++) {
Thread.sleep(100);
count++;
}
System.out.println(name + "号线程--" + Thread.currentThread().getName() + "--计数完成了");
countDownLatch.countDown();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
输出结果:
1号线程--Thread-1--计数完成了
0号线程--Thread-0--计数完成了
4号线程--Thread-4--计数完成了
2号线程--Thread-2--计数完成了
3号线程--Thread-3--计数完成了
计数完毕了,Thread-5等待线程执行
计数完毕了,Thread-6等待线程执行
计数完毕了,Thread-7等待线程执行
计数完毕了,Thread-8等待线程执行
计数完毕了,Thread-9等待线程执行
通过上面的例子可以看到,利用CountDownLatch的countDown方法与await()方法,我们可以同步计数线程与等待线程,使等待线程在所有计数线程完成后再开始运行。
二、CountDownLatch源码分析
接下来我们对countDownLatch内部源码进行一下分析。
1、CountDownLatch的构造。
首先看下CountDownLatch的构造函数
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
CountDownLatch的构造函数会接收一个count值做为计数器,也就是如果你需要等待N个线程执行结束,那这里就传入N。同时CountDownLatch会实例化一个Sync对象,这个Sync其实是CountDownLatch内部定义的一个继承自AbstractQueuedSynchronizer的实现类,所以CountDownLatch提供的同步和其他功能都是围绕Sync这个子类实现的,也就是基于AbstractQueuedSynchronizer类来实现的。
我们来看下Sync这个类的定义
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
/**
1、设置AbstractQueuedSynchronizer中同步状态的值state,也就是计数器的值。
2、这个值volatile变量,必须保证线程间的可见性;
**/
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
//获取同步状态的值
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
// 通过CAS操作改变同步状态值,保证同步状态的值state的线程安全。
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
2、countDown方法
首先我们看下countDown方法的源码
public void countDown() {
//改变同步状态值,线程执行完成时计数器减一
sync.releaseShared(1);
}
AbstractQueuedSynchronizer类中releaseShared() 方法的源码
public final boolean releaseShared(int arg) {
// CountDownLatch定义的子类Sync实现,通过CAS操作改变State的值
if (tryReleaseShared(arg)) {
//State以递减为0,代表着所有执行线程执行完毕,共享模式下释放锁,那么等待线程就能够拿到锁往下执行。
doReleaseShared();
return true;
}
return false;
}
当调用CountDownLatch的countDown方法时,就会执行计数器进行减一操作,直到所有线程全部执行完毕,计算器为0时唤醒等待线程。
AbstractQueuedSynchronizer中doReleaseShared方法是执行共享模式下释放锁的操作,从而让等待线程获取锁,继续向下执行。
3、await方法
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
AbstractQueuedSynchronizer类中acquireSharedInterruptibly() 方法的源码
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//获取同步状态值
if (tryAcquireShared(arg) < 0)
//同步状态值即计数器的值不为0,等待线程共享模式下尝试获取锁,获取不到锁的话进入阻塞
doAcquireSharedInterruptibly(arg);
}
await方法的实现也很明确,首页获取同步状态也就是计数器的值,如果为0即所有线程执行完毕返回1,否则返回-1的话,等待线程在共享模式下尝试获取锁,获取不到锁的话进入阻塞;
AbstractQueuedSynchronizer中doAcquireSharedInterruptibly方法是执行共享模式下获取锁的操作;
三、总结
通过上面分析可以看到CountDownLatch是基于AbstractQueuedSynchronizer类实现的,一个非常实用的多线程控制工具类,它类似与一个计数器用来控制指定的线程等待,直到计数器归零。以上我们对CountDownLatch类的使用与核心方法的源码进行了一定的分析,其中如有不足与不正确的地方还望指出与海涵。
关注微信公众号,查看更多技术文章。

Java多线程同步工具类之CountDownLatch的更多相关文章
- Java多线程同步工具类之Semaphore
Semaphore信号量通常做为控制线程并发个数的工具来使用,它可以用来限制同时并发访问资源的线程个数. 一.Semaphore使用 下面我们通过一个简单的例子来看下Semaphore的具体使用,我们 ...
- Java多线程同步工具类之CyclicBarrier
一.CyclicBarrier使用 CyclicBarrier从字面上可以直接理解为线程运行的屏障,它可以让一组线程执行到一个共同的屏障点时被阻塞,直到最后一个线程执行到指定位置,你设置的执行线程就会 ...
- Java多线程并发工具类-信号量Semaphore对象讲解
Java多线程并发工具类-Semaphore对象讲解 通过前面的学习,我们已经知道了Java多线程并发场景中使用比较多的两个工具类:做加法的CycliBarrier对象以及做减法的CountDownL ...
- 【同步工具类】CountDownLatch闭锁任务同步
[同步工具类]CountDownLatch闭锁任务同步 转载:https://www.cnblogs.com/yangchongxing/p/9214284.html 打过dota的同学都知道,多人一 ...
- java 利用同步工具类控制线程
前言 参考来源:<java并发编程实战> 同步工具类:根据工具类的自身状态来协调线程的控制流.通过同步工具类,来协调线程之间的行为. 可见性:在多线程环境下,当某个属性被其他线程修改后,其 ...
- 【同步工具类】CountDownLatch
闭锁是一种同步工具类,可以延迟线程的进度直到其达到终止状态. 作用:相当于一扇门,在到达结束状态之前,这扇门一直是关闭的,并且没有任务线程能够通过,当到达结束状态时,这扇门会打开并允许所有的线程通过, ...
- 深入分析同步工具类之CountDownLatch
概览: CountDownLatch又称闭锁,其作用是让一个或者多个线程挂起,直到其他的线程执行完后恢复挂起的线程,使其继续执行.内部维护着一个静态内部类Sync,该类继承AbstractQueued ...
- Java多线程并发工具类
Semaphore-信号灯机制 当我们创建一个可扩展大小的线程池,并且需要在线程池内同时让有限数目的线程并发运行时,就需要用到Semaphore(信号灯机制),Semaphore 通常用于限制可以访问 ...
- Java并发——同步工具类
CountDownLatch 同步倒数计数器 CountDownLatch是一个同步倒数计数器.CountDownLatch允许一个或多个线程等待其他线程完成操作. CountDownLatch对象 ...
随机推荐
- Qt中(图片)资源的三种使用方式
Qt中使用图片资源的方法有很多种,以前我一直分不清各种之间的区别和Qt相应的处理机制,后来遇到一些实际的问题,然后再加上查阅源码和资料,总算弄明白一些事情,但是本文仅仅是个人理解,如有错误之处请告诉我 ...
- node lesson2
var express = require('express'); var utility = require('utility'); var app = express(); app.get('/' ...
- openresty: nginx worker不同请求之间共享数据
To globally share data among all the requests handled by the same nginx worker process, encapsulate ...
- 分析MySQL各项指标
MySQL各项指标(因为这不是大多数搜索引擎的区别故意) INDEX(总指数):主要指标,不管是什么限制 ALTER TABLE `table_name` ADD INDEX index_name ( ...
- WPF loading加载动画库
原文:WPF loading加载动画库 1. 下载Dll https://pan.baidu.com/s/1wKgv5_Q8phWo5CrXWlB9dA 2.在项目中添加引用 ...
- Qt调用PolarSSL库(一个)
最近一直在学习SSL相关知识,也明白了理论相关知识,主要SSL基本概念和连接建立.主要依据PolarSSL开源库学习.学习完了之后就希望能给有所运用,就想用Qt写一个简单的程序,添加对SSL相关概念的 ...
- WCF Rest用法
GET GET单参数 服务器 [OperationContract] string GetOneParameter(string value); [WebInvoke(Method = "G ...
- C# WindowsPrincipal(Windows规则)的使用
using System;using System.Collections.Generic;using System.Linq;using System.Security.Principal;usin ...
- Grokking PyTorch
原文地址:https://github.com/Kaixhin/grokking-pytorch PyTorch is a flexible deep learning framework that ...
- Winform入门见解
winform算是C#比较快速的入门的一个了,简单的控件拖拽然后写上每个控件对应的事件.然后就可以了.需要美观的点 可以用Skin皮肤就完成了.我们先不说复杂的,就来个普通的三层架构来增删改查 分页和 ...