java多线程并发之CountDownLatch
CountDownLatch : 主线程同时启动所有子线程,等待所有子线程都执行完毕,才重新执行主线程;其内部的计数器继承了AQS,AQS内部维持了一个volatile变量 state,用来表示同步状态,
(1) CountDownLatch(int count) 初始化计数器:当执行CountDownLatch downCountDownLatch = new CountDownLatch(5)时,已初始化一个基于AQS的同步队列,并把传进来的计数器赋给AQS队列的stat【setState(count)】,stat的值也表示一个针对CountdownLatch当前剩余的计数次数;
// 继承了AQS
private static final class Sync extends AbstractQueuedSynchronizer {...}
/**
* 创建一个值为count的计数器
*/
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
// Sync 是CountDownLatch的一个内部类
this.sync = new Sync(count);
}
Sync(int count) {
// 继承自AQS
setState(count);
}
(2)void await():阻塞当前进程:
当调用await的时候,当前线程会被阻塞,同时加入到AQS阻塞队列,实际上是调用了AQS的acquireSharedInterruptibly方法,当线程调用了await方法时,该线程将被阻塞,直到下面两种情况之一:
(a)当所有线程都调用了countDown方法,也就是计数值为0时,
(b)当前线程的interrupted()方法被其他线程调用,就会抛出InterruptedException异常
/**
* 阻塞当前进程,将当前进程加入阻塞队列
*/
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
//AQS获取共享资源时可被中断的方法,
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
//如果线程被中断则抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//查看当前计数器值是否为0,
if (tryAcquireShared(arg) < 0)
// 调用AQS的doAcquireSharedInterruptibly方法让当前线程阻塞
// 实际上会构建阻塞队列的双向链表,挂起当前线程
doAcquireSharedInterruptibly(arg);
}
//sync类实现的AQS的接口
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
线程获取资源时可以被中断,并且获取的资源是共享资源.
(3)boolean await(long timeout, TimeUnit unit)
和public void await()区别仅在于当线程阻塞时间超过timeout时,会返回false,计数器为0时,则返回true,发生中断会抛出异常,而void await()方法没有返回值
(4)void countDown():计数器减1,当计数值为0时,则释放所有因调用await方法阻塞的线程,否则什么都不做
public void countDown() {
//委托sync调用AQS的方法
sync.releaseShared(1);
}
//AQS的方法
public final boolean releaseShared(int arg) {
//调用sync实现的tryReleaseShared
if (tryReleaseShared(arg)) {
//AQS的释放资源方法
doReleaseShared();
return true;
}
return false;
}
// Syc 实现的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;
// 调用AQS的cas
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
调用countDown()方法时是原子性递减AQS的state状态值,CountDownLatch中的Sync会优先尝试修改state的值,来获取同步状态。例如,如果某个线程成功的将state的值从0修改为1,表示成功的获取了同步状态。 这个修改的过程是通过CAS完成的,所以可以保证线程安全。
反之,如果修改state失败,则会将当前线程加入到AQS的队列中,并阻塞线程。
当CountDownLatch因异常导致计数值不能正常递减,最终到达0时,相应实例上的线程就会一直处于WAITING状态,有两种方式可避免出现这种现象:(a)调用countDown()方法时,尽量放在finally代码块内;(b)使用CountDownLatch.await(long,TimeUnit)方法,超时之后等待线程会自动唤醒
(5)long getCount():当前的计数器值
/**
* 返回当前计数器的值
*/
public long getCount() {
return sync.getCount();
}
// 其内部还是调用了 AQS 的getState来获取state值
int getCount() {
return getState();
}
CountDownLatch 是线程安全的
CountDownLatch和join的区别
使用join方法必须等待多个线程执行完毕之后才能解除阻塞状态,而CountDownLatch相对灵活,可以通过调用countDown方法来减少计数,唤醒被阻塞的线程
参考的实例
package com.example.lettcode.concurrent;
import cn.hutool.core.util.RandomUtil;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* @Class CountDownLatchDemo
* @Description TODO
* @Author
* @Date 2021/4/8
**/
public class CountDownLatchDemo {
public static void main(String[] args) {
int startCount = 1;
CountDownLatch startCountDownLatch = new CountDownLatch(startCount);
int downCount = 5;
CountDownLatch downCountDownLatch = new CountDownLatch(downCount);
for (int i = 0; i < downCount; i++) {
new Thread(new WorkerRunnable(startCountDownLatch, downCountDownLatch), "线程:" + i).start();
}
try {
// 休眠1s ,保证所有的线程都已经调用了await方法,进入阻塞状态
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("所有线程都已启动!!!");
// 唤醒所有阻塞的子线程
startCountDownLatch.countDown();
System.out.println("等待所有线程执行结束!!!");
try {
// 此处是为了让主线程阻塞,等待所有子线程执行完毕
downCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("所有任务都已执行结束!!!");
}
static class WorkerRunnable implements Runnable {
private CountDownLatch startCountDownLatch;
private CountDownLatch downCountDownLatch;
public WorkerRunnable(CountDownLatch startCountDownLatch, CountDownLatch downCountDownLatch) {
this.startCountDownLatch = startCountDownLatch;
this.downCountDownLatch = downCountDownLatch;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 已经准备好。。。");
try {
// 等待所有子线程都准备完毕再开始
startCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
childThreadWork();
try {
downCountDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
private void childThreadWork() {
System.out.println(Thread.currentThread().getName() + "开始执行");
try {
TimeUnit.SECONDS.sleep(RandomUtil.randomInt(5));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}