使用案例:
/**
* CountDownLatch 使用以及原理分析
*/
public class CountDownLatchDemo {
public static void main(String[] args) throws Exception {
CountDownLatch latch = new CountDownLatch(3);
ExecutorService service = Executors.newFixedThreadPool(3);
for (int i = 0;i < 3; i ++){
service.execute(()->{
System.out.println("当前线程名称:" + Thread.currentThread().getName());
latch.countDown();
});
}
System.out.println("主线程前开始执行!");
//当Count的数量不为0的时候这里阻塞 为0的时候才会方行 阻塞await后面的程序
latch.await();
System.out.println("主线程后开始执行!");
service.shutdown();
}
}
分析:
CountDownLatch latch = new CountDownLatch(3);//设置计数器
/**
* The synchronization state.
*/
private volatile int state;
protected final void setState(int newState) {
state = newState;
}
设置状态:初始化状态,该状态保存了计数器的个数
latch.countDown();
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {//状态校验 若为0表示计数释放完成
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; //状态建议
if (compareAndSetState(c, nextc))//CAS操作 设置状态的值
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;
if (ws == Node.SIGNAL) {//SIGNAL 表示需要唤醒它的继承者 也就是下一个节点
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h); //唤醒节点的下一个节点 unpark操作
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
1.状态校验 若为0则直接返回,不为0则状态减一,并给state设置值,返回最新的状态值
2.若状态值为0则释放共享锁。
latch.await();
/**
* Acquires in shared interruptible mode.
* @param arg the acquire argument
*/
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);//1 表示state为0可以释放锁 否则为-1
if (r >= 0) {
setHeadAndPropagate(node, r);//设置头结点并释放锁
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())//调用park 阻塞线程
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
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 either before
* or after setHead) 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 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared(); //释放锁 同上
}
}
1.构建一个共享队列
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;
}
//尾节点为null 时 头尾节点为同一个节点(new 的)然后再把共享节点作为尾节点添加到队列里面
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;
}
}
}
}
2.判断state状态
若为0:则释放锁,不为0则调用park方法阻塞当前线程。
使用场景:一个任务A,它要等待其他n个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。