CountDownLatch
等待所有线程执行完毕
CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务B执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。
CountDownLatch
类
仅有且必需, 需要有参数的构造方法,
初始化倒数次数
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
//Sync 看到这setState, 就有那感觉是利用CAS无锁算法实现的
Sync(int count) { setState(count); }
重点方法
CountDownLatch
类中
/**
* 将计数器的值进行减一, 一直减到0 如果当前计数等于0,那么什么也不会发生。
*
* @author: ZhiHao
* @date: 2021/6/3
*/
public void countDown() {
sync.releaseShared(1);
}
--------------------------------------下面方法调用链---------------------------------------------
// sync.releaseShared(1)
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
// 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;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
从
tryReleaseShared
方法可以看出, 并没有使用到releases
数据参数, 而且是个循环自旋, 当获取的State
值为0时候, 返回false, 跳出自旋, 如果不是0, 则减1, 然后调用底层C方法, 比较并设置State
值, (也就是CAS无锁算法设置值), 因为同时有多个线程可能进行减, 如果CAS设置失败的话会继续自旋, 继续获取最新的State
值进行减1, 继续设置, 设置成功则跳出自旋,。
// 当前线程进行阻塞, 等待计数器值减到0
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
// 有限时间的阻塞, 超过则不在阻塞
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
--------------------------------------下面方法调用链---------------------------------------------
// sync.acquireSharedInterruptibly
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
// tryAcquireShared(arg)-CountDownLatch
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
// AQS.doAcquireSharedInterruptibly 只有State值不等于0, 才会调用
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) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
从上面可以看出, 进行
await
方法阻塞的时候, 如果获取到State
值==0, 直接就结束了阻塞, 继续后面执行,如果获取到
State
值!=0, 则会不断自旋进行阻塞, 并State
值==0, 结束阻塞
进行测试
@Test
public void contextLoadss() throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(2);
executor.execute(() -> {
try {
System.out.println("执行耗时需要5秒");
TimeUnit.SECONDS.sleep(5);
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
executor.execute(() -> {
try {
System.out.println("执行耗时需要8秒");
TimeUnit.SECONDS.sleep(8);
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
executor.execute(() -> {
try {
countDownLatch.await();
System.out.println("我需要等上面2个线程执行完毕才执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
TimeUnit.SECONDS.sleep(10);
System.out.println("全部完毕执行");
}
//--------------------------------------------------------
执行耗时需要5秒
执行耗时需要8秒
我需要等上面2个线程执行完毕才执行
全部完毕执行
1