CountDownlatch源码分析
CountDolwnlatch(闭锁)是Java并发包中提供的并发工具之一,可以让多个线程同时等待其他线程完成后在进行下一步操作。具体使用场景以后分析。
先看看简单使用例子
public class Demo {
private static CountDownLatch countDownLatch=new CountDownLatch(2);
public static void main(String[] args) {
new Thread(()->cook()).start(); // 开始煮饭
new Thread(()->BrushTooth()).start(); //开始刷牙
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
goSchool();
}
public static void cook(){
System.out.println("煮饭30分钟");
countDownLatch.countDown();
}
public static void BrushTooth(){
System.out.println("刷牙10分钟");
countDownLatch.countDown();
}
public static void goSchool(){
System.out.println("去学校");
}
}
由例子可知,在Main方法调用await时,如果其他两个线程没有执行完毕countDown(),那么Main会等待完成后继续执行,接下来对源码进行分析。
源码结构如图所示:包括一个同步器,一个构造方法,定时和不定时的await方法,countDown方法,获取getCount方法。
1.构造方法
// 主要创建内部实现Sync public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); }
private static final class Sync extends AbstractQueuedSynchronizer { Sync(int count) { setState(count);// 将AQS中State设置为count值 } int getCount() { return getState(); } protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1;// 当state值为0的时候 } 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; } } }
由源码可知,构造方法就是new出同步器,然后设置state值。
2.await方法
此方法主要用于让当前线程等待,直到其他线程调用countDown()到一定数目才唤醒,源码如下:
public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1);// 调用AQS acquireSharedInterruptibly 可中断共享 }
// AQS模板方法 public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) //如果线程中断,爆出异常 throw new InterruptedException(); if (tryAcquireShared(arg) < 0) // 调用Countdownlatch中syn 实现方法 doAcquireSharedInterruptibly(arg); }
protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1; // 当state不等于0将调用doAcquireSharedInterruptibly()方法 }
由上述源码可以得出,当如果调用await()时state值已经为0将直接结束,当前线程继续执行。如果不为0将进入AQS源码doAcquireSharedInterruptibly中,这里不对此源码进行分析,后续AQS分析时在继续分析,doAcquireSharedInterruptibly()大概功能会自旋尝试获取到资源,如果获取到了,那么将返回值,然后await方法结束,如例子中是Main线程,将继续执行main方法后面的goSchool(),如果获取不到会被阻塞等待被唤醒。
await方法流程图,如下所示,其中自旋与阻塞部分由AQS框架实现,这里只简单描述。
3.await定时方法
此方法与await方法相比不同在于可以设置一个时间,如果在等待了当前时间比如5s后其他线程还没执行完毕,那么不再等待。定时原理留在AQS中分析。
4.countDown方法
public void countDown() { sync.releaseShared(1);//用于减少当前state值1 }public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { //调用内部实现Syn的方法作为是否唤醒的条件 doReleaseShared(); return true; } return false; }protected boolean tryReleaseShared(int releases) { for (;;) { int c = getState(); if (c == 0) //state已经为0时再次调用直接返回false,不影响程序 return false; int nextc = c-1; if (compareAndSetState(c, nextc)) //CAS更新state值 return nextc == 0; } }
countDown方法就是让state值减1。当state为0时,调用AQS中doReleaseShared方法,将阻塞的节点唤醒。
5.小结
countdownlatch就是通过实现AQS,通过state值来控制需等待的线程数。