使用
- 构造时,指定一个int 门栓个数
- 线程A 执行cdt.await(),线程A会park 挂起
- 线程B 调用 cdt.coundDown(); 会打开一个门栓,直到门栓个数是0时,notify 所有park 的线程
主要方法
class CountDownLatch {
public CountDownLatch(int count) {}
public await() {}
public coundDown() {}
}
原理
AbstractQueuedSynchronizer实现的
创建
class CDL {
public CDL(int count) {
. // count必须>0
sync = new Sync(count);
}
class Sync extends AQS {
Sync(int count) {
setState(count);
}
}
}
await()
class CDL {
void await() {
sync.acquireSharedInterruptibly(1); //进入AQS里
}
}
class AQS {
// arg 没有用到。门栓数是保存到AQS.state里。
// 判断门栓个数,为0则结束;不为0则 线程park
void acquireSharedInterruptibly(int arg) {
if (Thread.interrupted()) {
throw new .;
}
if () { // 如果门栓是0,则不进入if分支,此函数会结束。
// 如果门栓数不是0,则进入if分支,线程park
doAcquireSharedInterruptibly(arg);
}
}
}
- 门栓 != 0时,线程在for循环里park
- 调用 countDown() 方法,门栓 = 0 时,unpark head.next里的线程。
- head.next里的线程从for 循环里 parkAndCheckInterrupt() 处 继续执行。
class AQS {
void doAcquireSharedInterruptibly(int arg) { //参数 arg 没有用
// 1. 新建Node,这个Node.nextWaiter是 Node.EXCLUSIVE,Node.thread=Thread.currentThread()
// 2. 把它添加到 队列尾巴 上。
// 队列没有node时,head 和 tail 还是会指向一个初始化的Node
Node node = addWaiter(Node.SHARED);
try {
for (;;) {
Node p = node.predecessor();
// unpark 唤醒后,如果本node == head.next,尝试执行
if (p == head) {
.
if (r >= 0) { // r >=0 的含义就是 门栓=0
// 把node设置成 head,并且再执行 doReleaseShared(),用以再unpark下一个thread
setHeadAndPropagate(node, r);
p.next = null;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) // node是本次的node,p=node.pre。true的条件是 p.wateStatus = Node.signal
&& parkAndCheckInterrupt()) { // 当前线程park,等待唤醒
throw new .;
}
}
}
}
}
countDown()
class CDL {
void countDown() {
sync.releaseShared(1); // AQS 里
}
}
- 门栓数=0时,unpark head.next里的线程
class AQS {
// 参数 arg 没用
boolean releaseShared(int arg) {
if (门栓-1后,是否为0) {
// 门栓=0 时
// unpark head.next里的线程
doReleaseShared();
return true;
}
return false;
}
}
- unpark head.next里的线程
class AQS {
void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus; // 在acquire 时,会把 head.waitStatus 设置为 Node.signal
if (ws == Node.signal) {
..
unparkSuccessor(h); // successor是接班人的意思。 unpark head.next里的thread
}
...
}
}
}
}
- unpark head.next里的线程,此时那个park的线程会在 doAcquireSharedInterruptibly() 里恢复并继续执行
class AQS {
void unparkSuccessor(Node node) {
...
Node s = node.next;
...
if (s != null) {
LockSupport.unpark(s.thread);
}
}
}