一个同步工具,使得当前线程等待,直到其他多个线程完成一系列的操作后,继续执行。
其中的当前线程也可以是多个线程,这个地方的线程的数量是任意的。
也就是await() 可以在多个线程中调用。
这个方法可以使得当前线程等待,也就是当前线程在未获得同步状态时,进入等待状态。因此这个这个也是获取同步状态的方法。这个方法获取同步状态,是以共享式的获取同步状态,因此await() 可以在多个线程中调用。
CountLatchDown的实现也是基于AbstractQueuedSynchronizer 同步器。
1 状态 state
state在CountDownLatch 起到一个计数器的作用,可以表示需要等待线程的数量。
当state==0时,await() 返回,然后继续执行后面的代码。当创建CountDownLatch时,初始化state的数量。
2 countDown 方法
public void countDown() {
sync.releaseShared(1);
}
每调用一次,同步器就会共享式的释放一个拥有同步状态的线程。state也相应的减一。
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
这个地方就使用了模板设计模式。上面这个方法是在同步器(父类)中定义的。
tryReleaseShared在不同的同步组件中有不同的实现,在CountDownLatch的实现如下:
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;
}
}
逻辑就是无限循环的尝试将 状态以线程安全的方式减一。
3 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);
}
这个方法也是定义在同步器(父类)中。
tryAcquireShared 共享式的获取同步状态,大于0时,表示获取成功。
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
表示未成功获取同步状态时,进入方法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) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
//创建共享节点 并加入队列
final Node node = addWaiter(Node.SHARED);
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;
}
先看下 Node的构造器:
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
也就是Node.SHARED 赋值给了 this.nextWaiter。
介绍下addWaiter的实现逻辑:
当队尾节点不为null时,尝试设置为队尾节点,有可能失败。如果成功直接返回。
失败进入enq方法:
private Node enq(final Node node) {
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;
}
}
}
}
当同步对列中没有任何结点时,
首先看下这个compareAndSetHead(new Node())这个方法,
这个方法一开始看的时候,没有看仔细,把 参数里面的new Node() 当成了前面传过来的node,导致一直不理解这个“操作”。后来突然发现这个是 新建的空节点 。
/**
* CAS head field. Used only by enq.
*/
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
设置head节点为空节点,tail=head。
后面这个方法compareAndSetTail 就没有问题了,就是以原子的方式追加到同步队列的末尾。
至此 final Node node = addWaiter(Node.SHARED);
执行完毕。
继续看下面的代码,下面是尝试获取同步状态的代码。 先看下,同步状态获取不成功的情况(一开始时,一般情况下,是不会马上就会获得同步状态的。)
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
初始情况下, waitStatus 为0;
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
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) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* 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;
}
所以执行的是 compareAndSetWaitStatus
把节点设置为SIGNAL状态,然后这个状态的节点随后会被设置,阻塞
也就是这个方法parkAndCheckInterrupt。
LockSupport.park(this);
然后我们看获取到同步状态的情况:
这种情况,是在countdown 方法进行了足够的执行之后,state==0时。
final Node p = node.predecessor();
返回的是前序节点。
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
如果前序节点为 head节点。尝试获取同步状态。
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {//获取成功
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);
/*
* Try to signal next queued node if:
* Propagation was indicated by caller,
* or was recorded (as h.waitStatus) by a previous operation
* (note: this uses sign-check of waitStatus because
* PROPAGATE status may transition to SIGNAL.)
* and
* The next node is waiting in shared mode,
* or we don't know, because it appears null
*
* The conservatism in both of these checks may cause
* unnecessary wake-ups, but only when there are multiple
* racing acquires/releases, so most need signals now or soon
* anyway.
*/
if (propagate > 0 || h == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}
/**
* Release action for shared mode -- signal successor and ensure
* propagation. (Note: For exclusive mode, release just amounts
* to calling unparkSuccessor of head if it needs signal.)
*/
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;
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;
}
}
因为节点的状态是是signal,然后将这个状态设置为0 ,如果成功了,执行unparkSuccessor,: LockSupport.unpark(s.thread); 这个就是唤醒节点的方法,和前面的方法LockSupport.park(this); 是相对应的。