Jdk1.6 JUC源码解析(9)-CountDownLatch
作者:大飞
功能简介:
- CountDownLatch是一种锁,称为闭锁。可以让一个或多个线程等待另外一个或多个线程执行完毕后再执行。
- CountDownLatch也是基于AQS构建,使用共享模式。
- CountDownLatch中提供一个count值来表示要等待的(其他任务)完成次数,常规用法有两种:Count(1)和Count(N)。举个栗子,百米赛跑,N个选手,每个选手可以看成是一个线程。起跑前,选手准备(线程启动,然后在Count(1)上阻塞),当发令枪响后(相当于Count(1)闭锁释放),选手一起起跑(相当于线程通过Count(1)继续执行),当所有选手都通过终点(相当于Count(N)闭锁释放),然后再统计成绩。
源码分析:
- CountDownLatch基于AQS构建,首先看下内部的同步器:
/**
* CountDownLatch内部同步器,利用AQS的state来表示count。
*/
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
//如果当前count为0,那么方法返回1,
//按照之前对AQS的分析,请求成功,并唤醒同步队列里下一个共享模式的线程(这里都是共享模式的)。
//如果当前count不为0,那么方法返回-1,请求失败,当前线程最终会被阻塞(之前会不止一次调用tryAcquireShared)。
return getState() == 0? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
// 如果count为0,返回false,相当于释放失败,因为此时闭锁处于开放状态,没有必要在打开。
// 如果count不为0,递减count。
// 最后如果count为0,说明关闭的闭锁打开了,那么返回true,后面会唤醒等待队列中的线程。
// 如果count不为0,说明闭锁还是处于关闭状态,返回false。
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
- 内部的实现也很简单了:
public class CountDownLatch {
private final Sync sync;
...
/**
* 根据给定的count创建一个CountDownLatch。
*/
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
/**
* 如果当前count为0,那么方法立即返回。
*
* 如果当前count不为0,那么当前线程会一直等待,直到count被(其他线程)减到0或者当前线程被中断。
*/
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
/**
* 如果当前count为0,那么方法立即返回true。
*
* 如果当前count不为0,那么当前线程会一直等待,直到count被(其他线程)减到0或者当前线程被中断或者超时。
* 成功返回true,超时返回false,被中断抛异常。
*/
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
/**
* 如果当前count大于0,递减count。如果递减后,count等于0,那么AQS中所有等待线程都被唤醒。
*
* 如果当前count等于0,什么事都不会发生。
*/
public void countDown() {
sync.releaseShared(1);
}
/**
* 获取当前count值。
*/
public long getCount() {
return sync.getCount();
}
/**
* Returns a string identifying this latch, as well as its state.
* The state, in brackets, includes the String {@code "Count ="}
* followed by the current count.
*
* @return a string identifying this latch, as well as its state
*/
public String toString() {
return super.toString() + "[Count = " + sync.getCount() + "]";
}
}
小总结一下:
1.建立一个count为n的闭锁后,闭锁的内部计数为n,这时如果有线程调用闭锁的await方法,会阻塞。
2.每一次调用闭锁的countDown方法,内部计数就会减1,当闭锁的countDown方法被调用n次后,内部计数减为0,这时在闭锁await方法上等待的线程就被唤醒了。
CountDownLatch的代码解析完毕!
参见:Jdk1.6 JUC源码解析(6)-locks-AbstractQueuedSynchronizer