CountDownLatch使用AQS中的共享锁实现
一个或多个线程等待一个或多完成之后再执行
同步值0是中间状态
await方法 同步状态的值大于0线程阻塞
countDown方法 同步状态的值减一 同步状态的值等于0 唤起所有等待的线程
重要构造
它就一个构造方法
/**
* 初始化内部属性Sync 同步器 设置同步状态的值
* @param count 同步状态的值
*/
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
Sync(int count) {
setState(count);//设置同步状态的值
}
/**
* 设置state AQS中的属性
*/
protected final void setState(int newState) {
state = newState;
}
重要方法
await
/**
* 尝试获取共享锁可中断对外提供的
* 内部同步器实现
* state != 0 阻塞线程
*/
public void await() throws InterruptedException {
//调用AQS中的模板方法
sync.acquireSharedInterruptibly(1);
}
/**
* 获取共享锁 可中断
*/
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
//当前线程被标记为中断的 直接抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//尝试获取共享锁 子类实现小于0
if (tryAcquireShared(arg) < 0)
//获取共享锁 AQS实现
doAcquireSharedInterruptibly(arg);
}
这个我们已经很熟悉了,是AQS中的模板方法,AQS中的细节我就不说了。
然后我们来看CountDownLatch对tryAcquireShared的具体实现,它是怎么样才会阻塞线程的吧。
可以看到和我们开头说的是一样的,状态值不为0,就会尝试阻塞当前线程,状态值为0了,不会阻塞。
countDown
再来看看与之对立的countDown
/**
* state减一
* 内部同步器实现
*/
public void countDown() {
//调用AQS中的模板方法
sync.releaseShared(1);
}
/**
* 释放共享锁
*/
public final boolean releaseShared(int arg) {
//尝试释放共享锁成功 子类实现
if (tryReleaseShared(arg)) {
//唤醒后继阻塞节点
doReleaseShared();
return true;
}
return false;
}
重点还是关注CountDownLatch对tryReleaseShared的实现,它对释放锁成功是怎么定义的。
可以看到,当状态值减到0的时候,它才算是释放锁成功,然后会唤醒所有因为调用await而阻塞的线程。
典型应用
某线程等待多个线程执行完毕,然后才开始执行。
public static void main(String[] args) throws InterruptedException {
int count = 5 ;
CountDownLatch countDownLatch = new CountDownLatch(count);
//线程池 核心线程数 3 最大线程数 5 大于核心线程数的线程空闲存活时间 200毫秒 阻塞队列 LinkedBlockingQueue 无限制的
ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 5, 200, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
for (int i = 0; i < count; i++) {
executor.execute(()->{
try {
Thread.sleep(1000);
System.out.println("执行完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
}
countDownLatch.await();
System.out.println("主线程执行完成");
//停掉线程池
executor.shutdown();
}
关于CountDownLatch的完整源码解析点击这里