CountDownLatch
可用于实现闭锁操作,
延迟线程的进度直到其到达终止状态。确保某些活动直到其它活动都完成后才继续运行,只开启一次。发令枪响后,所有运动员跑步
//这个节点由于超时或中断被取消了。节点不会离开(改变)这个状态。尤其,一个被取消的线程不再会被阻塞了
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
/* 这个节点的后继(或者即将被阻塞)被阻塞(通过park阻塞)了,所以当前节点需要唤醒它的后继当它被释放或者取消时。
为了避免竞争,获取方法必须首先表示他们需要一个通知信号,然后再原子性的尝试获取锁,如果失败,则阻塞。
也就是说,在获取锁的操作中,需要确保当前node的preNode的waitStatus状态值为’SIGNAL’,才可以被阻塞,当
获取锁失败时。(『shouldParkAfterFailedAcquire』方法的用意就是这)*/
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
/* 这个节点当前在一个条件队列中。它将不会被用于当做一个同步队列的节点直到它被转移到同步队列中,
转移的同时状态值(waitStatus)将会被设置为0。
(这里使用这个值将不会做任何事情与该字段其他值对比,只是为了简化机制)。*/
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
//一个releaseShared操作必须被广播给其他节点。(只有头节点的)该值会在doReleaseShared方法中被设置去确保持续的广播,即便其他操作的介入。
static final int PROPAGATE = -3;
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
//构造一个用给定计数初始化的 CountDownLatch。
this.sync = new Sync(count);
}
Sync(int count) {
//设置同步状态的值。
setState(count);
}
await
public void await() throws InterruptedException {
//以共享模式获取对象,如果被中断则中止。
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
//中断检测
if (Thread.interrupted())
throw new InterruptedException();
//个数是否消耗完,消耗完则直接跳过,直接执行
//否则执行下列方法
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
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);
//释放GC
p.next = null; // help GC
//标记正确执行
failed = false;
return;
}
}
//检查并修改一个节点的状态,当该节点获取锁失败时。返回true如果线程需要阻塞。
if (shouldParkAfterFailedAcquire(p, node) &&
//这里执行阻塞
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private Node addWaiter(Node mode) {
//包装当前线程以及表示该节点正在共享模式下等待
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
//将当前节点添加到尾部节点
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//设置尾部节点,以及节点初始化
enq(node);
return node;
}
private Node enq(final Node node) {
//(自旋+CAS)
for (;;) {
Node t = tail;
//初始化,头和尾为空节点
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
///设置尾部节点
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
//设置当前节点为头节点,以及清空当前线程和前置节点
setHead(node);
//如果闭锁是开的,且头为null,且waitStatus
//如果标识了广播(propagate>0),
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
//如果当前节点为null或者当前节点为共享等待标记,则释放,执行
//enq初始化时,会有短暂为null现象
if (s == null || s.isShared())
doReleaseShared();
}
}
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
//更新默认值0,且解除park
if (ws == Node.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) // loop if head changed
break;
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
//则说明node的前驱节点已经被要求去通知释放它的后继节点,所以node可以安全的被挂起(park)
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
//则说明node的前驱节点被取消了。那么跳过这个前驱节点并重新标志一个有效的前驱节点(即,
// waitStatus <= 0 的节点可作为有效的前驱节点),然后,退出方法,返回false。
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//其他情况下,即pred.waitStatus为’0’或’PROPAGATE’。
// 表示我们需要一个通知信号(即,当前的node需要唤醒的通知),
// 但是当前还不能挂起node。
// 调用『compareAndSetWaitStatus(pred, ws, Node.SIGNAL)』方法通过CAS的方式来修改前驱节点的waitStatus为“SIGNAL”。
// 退出方法,返回false。
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
//等待状态值指示后续线程需要断开连接
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
private final boolean parkAndCheckInterrupt() {
//为了线程调度,在许可可用之前禁用当前线程。
LockSupport.park(this);
//测试当前线程是否已经中断。
return Thread.interrupted();
}
countDown
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
//检测闭锁是否开启
if (tryReleaseShared(arg)) {
//释放
doReleaseShared();
return true;
}
return false;
}
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;
//修改c=c-1,当=0时,直接返回
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
//广播释放所有阻塞的锁
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
//更新默认值0,且解除park
if (ws == Node.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) // loop if head changed
break;
}
}
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
//重置状态值
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
Node s = node.next;
//如果下一个节点为null,且状态取消了,则从尾端遍历,查找未取消的线程
//所以当看到next字段为null时并不意味着当前节点是队列的尾部了。
//无论如何,如果一个next字段显示为null,我们能够从队列尾向前扫描进行复核。
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
释放后,这里的阻塞方法会解除阻塞,向下执行
为什么node.next可能为null?
cancelAcquire
private void cancelAcquire(Node node) {
// Ignore if node doesn't exist
if (node == null)
return;
node.thread = null;
// Skip cancelled predecessors
//将node的prev属性指向一个在它之前的有效的节点
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
// predNext is the apparent node to unsplice. CASes below will
// fail if not, in which case, we lost race vs another cancel
// or signal, so no further action is necessary.
/* 这个predNext是pred表面上的下一个连接的节点(即,无需考虑该节点是否被取消了)。
下面的CAS操作将会失败(『compareAndSetNext(pred, predNext, null);』or『compareAndSetNext(pred, predNext, next);』)
,如果和其他的取消或通知操作发生竞争时,这时不需要进一步的操作。因为如果产生竞争,
说明pred的next已经被修改了,并且是最新的值了,而我们的操作也就没有要执行的必要了。*/
Node predNext = pred.next;
// Can use unconditional write instead of CAS here.
// After this atomic step, other Nodes can skip past us.
// Before, we are free of interference from other threads.
/* 将node的waitStatus设置为’CANCELLED’。这里可以使用无条件的写代替CAS(注意,node的waitStatus是volatile的)。
在这个原子操作之后,其他节点会跳过我们(即,跳过waitStatus被置位CANCELLED的节点),
在这个原子操作之前,我们不受其他线程的干扰。也就是说,无论其他线程对node的waitStatus是否有在操作,
在当前的情况下我们都需要将这个node的waitStatus置为’CANCELLED’。*/
node.waitStatus = Node.CANCELLED;
// If we are the tail, remove ourselves.
/* 如果待取消的node节点是队列尾节点的话(即,『node == tail』),那么删除node自己即可。使用CAS将tail节点设置成前面得到的第一个有效前驱节点
(即,『compareAndSetTail(node, pred)』)。并且CAS操作成功的话,
执行『compareAndSetNext(pred, predNext, null);』
也就是将tail的next置为null的意思。如果该CAS操作失败的话,
没关系。说明此时tail已经被修改了。*/
if (node == tail && compareAndSetTail(node, pred)) {
compareAndSetNext(pred, predNext, null);
} else {
// If successor needs signal, try to set pred's next-link
// so it will get one. Otherwise wake it up to propagate.
int ws;
if (
//pred不是head节点
pred != head &&
//pred.waitStatus为SIGNAL” 或者
((ws = pred.waitStatus) == Node.SIGNAL ||
//“pred.waitStatus <= 0”时且通过CAS将pred.waitStatus设置为SIGNAL”成功
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
//pred的thread非空
pred.thread != null) {
Node next = node.next;
/* 当node的next节点非空,且next节点的waitStatus<=0(说明next节点未被取消)时,
通过CAS将pred的next执行node的next(即,pred.next = node.next)。
同时,如果该CAS操作失败是没关系的,说明有其他线程操作已经修改了该pre的next值。*/
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
//释放当前这个待取消节点的下一个节点。
//当prev是head节点,或者prev也被取消的话,会执行『unparkSuccessor(node);』来释放node的下一个节点,其实也就是pred的下一个节点)
unparkSuccessor(node);
}
//释放GC
node.next = node; // help GC
}
}
总结
依次按照链表顺序执行,当设置共享模式的时候,广播唤醒,
唤醒的节点中途取消了,则从尾端遍历寻找下一个可以唤醒的节点