今天在学习ZooKeeper的实例的时候,发现了CountDownLatch这个类,那么这个类又是如何使用的呢?以及这个类的原理是什么?我们首先看一下简单的demo:
public class CountDownLatchTest {
static class Student implements Runnable {
CountDownLatch countDownLatch = null;
/**
*
*/
public Student(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
/*
* (non-Javadoc)
*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try {
Thread.sleep( 1000 );
System.out.println( "交卷了!" );
countDownLatch.countDown();
} catch (Exception e) {
// TODO: handle exception
}
}
}
static class Teacher implements Runnable {
CountDownLatch countDownLatch = null;
/**
*
*/
public Teacher(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
/*
* (non-Javadoc)
*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try {
countDownLatch.await();
System.out.println( "完成考试了" );
} catch (Exception e) {
// TODO: handle exception
}
}
}
public static void main(String args[]) {
CountDownLatch countDownLatch = new CountDownLatch( 4 );
Executor executor = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors() + 1 );
executor.execute( new Student( countDownLatch ) );
executor.execute( new Student( countDownLatch ) );
executor.execute( new Student( countDownLatch ) );
executor.execute( new Student( countDownLatch ) );
executor.execute( new Teacher( countDownLatch ) );
}
}
如上面所示,我们首先定义了定义了一个Student的线程,然后在休眠一段时间,执行CountDownLatch.countDown(),然后定义了Teacher的线程,直接等待。CountDownLatch的值降低到0自动会唤醒。
其实CountDownLatch类似于一个计数器,只不过这个计数器同时只有一个线程去操作。
这个类适用于当某个线程等待其他n个线程完成任务的时候再执行相应的任务。
那么CountDownLatch的实现原理是什么呢?我们来看一下源码,当我们初始化CountDownLatch的时候会发生什么事情:
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count <0");
this.sync = new Sync(count);
}
CountDownLatch在初始化的时候,将内部变量sync进行赋值,sync的变量类型是Sync,那么我们看看Sync的实现
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) {
return (getState() == 0) ? 1 : -1;
}
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;
}
}
}
我们可以看到Sync也是基于AQS实现的共享锁。那么当调用CountDownLatch.countDown()会发生什么?
public void countDown() {
sync.releaseShared(1);
}
可以看出来,CountDownLatch.countDown()就是调用AQS的releaseShared(int arg),然后releaseShared(int arg)可以保证将数量原子地减一。
那么CountDownLatch.await()又是如何实现的呢?
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
await()调用的是sync的acquireSharedInterruptibly(int arg)
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
在Sync重写了tryAcquireShard(arg),当计数器数字不为0的时候返回-1,因此调用doAcquireShardInterruptibly(arg),而doAcquireShardInterruptibly则会将本线程加载到执行链条的末尾,当别的线程执行完后才会执行,关于AQS的源码解析我们下个文章再看。