CountDownLatch源码解析
CountDownLatch 允许一个或多个线程等待其他线程完成操作。
使用示例
public class CountDownLatchTest {
public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(2);
System.out.println("主线程开始执行…… ……");
//第一个子线程执行
ExecutorService es1 = Executors.newSingleThreadExecutor();
es1.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
System.out.println("子线程:"+Thread.currentThread().getName()+"执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
}
});
es1.shutdown();
//第二个子线程执行
ExecutorService es2 = Executors.newSingleThreadExecutor();
es2.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程:"+Thread.currentThread().getName()+"执行");
latch.countDown();
}
});
es2.shutdown();
System.out.println("等待两个线程执行完毕…… ……");
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("两个子线程都执行完毕,继续执行主线程");
}
}
下面来看他的源码
重要内部类
/* **
* 继承了AQS的类,用于负责CountDownLatch的同步事件
*/
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
//设定我们的state,
Sync(int count) {
setState(count);
}
//获取我们的state
int getCount() {
return getState();
}
//在我们调用await的时候会调用这个方法,重要
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
//在我们调用countdown的时候会调用这个方法
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
重要属性
//包含一个Sync,用于实现同步事件
private final Sync sync;
构造函数
//构造函数,会初始化一个Sync
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
重要方法
await():用于等待其他线程执行完成
/* **
* 用于等待到count变为0
* 如果当前count大于0,当前线程将会wait,直到count等于0或者中断。
* PS:当count等于0的时候,
* 再去调用await(),线程将不会阻塞,而是立即运行
*/
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
//AQS的方法
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
//如果线程被中断了,抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//尝试去获取n个资源,小于0获取失败
//为0就不需要加入到阻塞队列
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
//Sync的方法
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
//AQS的方法
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);
//如果getState为0,说明count为0,等待的线程可以执行了
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.await()的步骤
- 调用Sync.acquireSharedInterrupt()方法
- 判断我们的state是不是已经是0了
- 是的话就不需要加入到同步队列
- state不是0,需要作为共享节点加入到同步队列,同时进行阻塞,直到被唤醒
下面是countdown()方法
/* **
* 递减锁存器的计数,如果计数到达零,则释放所有等待的线程
* 会通过CAS来进行修改state的值
*/
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
//尝试释放共享锁
if (tryReleaseShared(arg)) {
//释放共享锁
doReleaseShared();
//state==0,所以从队列里面一个接一个的退出
return true;
}
return false;
}
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
private void doReleaseShared() {
for (;;) {
AbstractQueuedSynchronizer.Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == AbstractQueuedSynchronizer.Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, AbstractQueuedSynchronizer.Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, AbstractQueuedSynchronizer.Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
countdown()的主要逻辑就是
- 调用Sync.releaseShared()方法
- 尝试释放共享锁,在这里会进行自旋,同时判断state是不是等于0,同时CAS我们的state,如果是自己CAS为了0,就需要自己去释放所有的节点,唤醒节点,否则返回false,由其他线程负责
- 释放共享锁,进行自旋然后一个一个的唤醒我们的节点