一、CountDownLatch简介
CountDownLatch是一个辅助同步器类,用来作计数使用,它的作用有点类似于生活中的倒数计数器,先设定一个计数初始值,当计数降到0时,将会触发一些事件,如火箭的倒数计时。
初始计数值在构造CountDownLatch对象时传入,每调用一次 countDown() 方法,计数值就会减1。
线程可以调用CountDownLatch的await方法进入阻塞,当计数值降到0时,所有之前调用await阻塞的线程都会释放。
注意:CountDownLatch的初始计数值一旦降到0,无法重置。如果需要重置,可以考虑使用CyclicBarrier。
二、CountDownLatch使用示例
1、作为一个开关/入口
package com.liaoxiang.multithreading3.middle.aqs_lock;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchTest2 {
public static void main(String[] args) {
CountDownLatch switcher = new CountDownLatch(1);
for (int i = 0; i < 3; ++i) {
new Thread(new Worker(switcher)).start();
}
doSomething();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程执行完成");
switcher.countDown(); // 主线程开启开关
}
public static void doSomething() {
System.out.println("主线程任务执行中...");
}
}
class Worker implements Runnable {
private final CountDownLatch startSignal;
public Worker(CountDownLatch startSignal) {
this.startSignal = startSignal;
}
@Override
public void run() {
try {
System.out.println("进入子线程");
startSignal.await(); //所有执行线程在此处等待开关开启
doWork();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
void doWork() {
System.out.println("子线程开始执行任务...");
}
}
2、作为一个完成信号
package com.liaoxiang.multithreading3.middle.aqs_lock;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchTest1 {
public static void main(String[] args) {
final CountDownLatch countDown = new CountDownLatch(2);
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("t1开始执行,等待其他线程处理完成...");
//线程执行到这里时阻塞,当有2个线程发出countDown.countDown()通知,此线程就继续
countDown.await();
System.out.println("t1线程继续执行...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("t2线程进行初始化操作...");
Thread.sleep(2000);
System.out.println("t2线程初始化完毕,通知t1线程继续");
countDown.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("t3线程进行初始化操作...");
Thread.sleep(4000);
System.out.println("t3线程初始化完毕,通知t1线程继续");
countDown.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
t2.start();
t3.start();
}
}
原理分析
可以看到CountDownLatch的实现同样是基于AQS,但是相比其他的同步器要简单很多
构造函数
从构造函数中可以看出,在创建CountDownLatch实例的时候,初始化了state状态变量,可以猜到本类中的await()
和countDown()
方法,就是在对state进行加减的过程中判断state的状态来最终达到CountDownLatch
所要实现的功能
//自身构造器
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
//内部类构造器
Sync(int count) {
setState(count);
}
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)
//staste不为 0时,执行(tryAcquireShared(arg)返回false)
doAcquireSharedInterruptibly(arg);
}
//CountDownLatch.Sync#tryAcquireShared
protected int tryAcquireShared(int acquires) {
// 计数state不为0时,返回-1
return (getState() == 0) ? 1 : -1;
}
// AbstractQueuedSynchronizer#doAcquireSharedInterruptibly
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED); // 入队
boolean failed = true;
try {
for (;;) {
// 获取前驱节点
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
// 获取锁成功,设置队列头为node节点
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node)
&& parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
countDown()
public void countDown() {
sync.releaseShared(1);
}
//AbstractQueuedSynchronizer#releaseShared
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
//CountDownLatch.Sync#tryReleaseShared
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState(); //获取state
if (c == 0) //如果state已经为0,则返回false
return false;
int nextc = c-1; //从当前值中-1
if (compareAndSetState(c, nextc))//CAS设置-1后的新值
//如果设置成功后state为0,返回ture,否在还是返回false
//即执行一次countDown可能还不足以达到阻塞线程执行的条件
return nextc == 0;
}
}
// AbstractQueuedSynchronizer#doReleaseShared
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
// 头结点如果为SIGNAL,则唤醒头结点下个节点上关联的线程,并出队
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)){
continue; // loop on failed CAS
}
}
if (h == head) // 没有线程被阻塞,直接跳出
break;
}
}
来源:CSDN
作者:Liao_Xiang
链接:https://blog.csdn.net/a2231476020/article/details/103646193