在我开门之前谁也别想动
CountDownLatch 是 Java Concurrent 包里边的一个同步工具类。它可以使一个或多个线程等待一个事件发生。
CountDownLatch 有一个计数器,表示等待的事件数量。以及一个 await 方法,当计数器一直大于零的时候,会一直等待。
一般有两种用法,创建一组线程,让他们并发的执行 或者 等待,直到所有的线程完成任务。当然用来做一些有先后顺序的工作也很好用。
await 方法#
一直追踪这个方法,在AbstractQueuedSynchronizer
有这段代码
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);
}
}
在 CountDownLatch 里有个内部类Sync
,实现了AbstractQueuedSynchronizer
, 在初始化 CountDownLatch 的时候,初始化 Sync,同时生成一个
队列(链表),根据 CountDownLatch(size)的 size,来生成 Node, 在最后一个就剩 head 之前会一直循环(Await)。
也就是说,调用 await 的时候,尝试获取共享锁,然后失败,因为节点不是 head, 所以会加入到 tail(CAS 方式添加)。
1#
CountDownLatch startLatch = new CountDownLatch(1);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
startLatch.await();
System.out.println(Instant.now());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
System.out.println("start...");
startLatch.countDown();
所有线程因为 count 是 1,被卡住,直到 countdown。
2#
CountDownLatch waitLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
int finalI = i;
new Thread(() -> {
try {
Thread.sleep(new Random().nextInt(2000));
System.out.println("i: " + finalI);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
waitLatch.countDown();
}
}).start();
}
waitLatch.await();
System.out.println("end");
最后输出end
被执行之前要先执行完之前的线程,因为 count>0, 如果 count 的初始值过小,就会提前输出最后的end
i: 8
i: 3
i: 6
i: 5
i: 0
end
i: 4
i: 1
i: 9
i: 7
i: 2