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的代码解析完毕!