CountDownLatch工具类的特点
CountDownLatch是一种通过计数器的方式来允许一个或者多个线程等待其他线程完成操作。通过这个特点可以实现对多个线程的执行顺序的控制。
源码解析
CountDownLatch中自定义同步器Sync
/**
* 使用AQS队列同步器来实现计数的
*/
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
//指定计数的构造器
Sync(int count) {
setState(count);
}
//采用AQS的volatile读的方式来获取同步状态值
int getCount() {
return getState();
}
//在共享模式下获取对象的状态,如果成功获取返回1,失败则返回负值
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
//在共享模式下释放对象的状态
protected boolean tryReleaseShared(int releases) {
//死循环方式
for (;;) {
//获取当前对象的同步状态
int c = getState();
//如果同步状态为0,那么说明需要释放锁的对象数为0,返回false
if (c == 0)
return false;
//当前同步状态值减1
int nextc = c-1;
//采用cas乐观锁的方式来将预期值和当前更新值进行比较
if (compareAndSetState(c, nextc))
//当前更新值为0,说明不存在需要释放的对象了。
return nextc == 0;
}
}
}
解释:可以发现CountDownLatch也是采用AQS这样一个抽象模板类来实现自定义的同步器。
解释①:自定义的同步器Sync采用了AQS中的共享锁实现的。
解释②:采用设置AQS的同步状态值初始化自定义同步器Sync。
解释③:是否可以获取共享锁,通过同步状态值是否为0来判断,如果为0,说明当前锁没人占有,返回1。反之有人占有,那么返回-1。
解释④:是否可以释放共享锁,通过自旋+cas来判断当前同步状态值是否为0,如果为0,那么说明已经没人占用共享锁了(释放成功),反之说明未释放完,返回false。
CountDownLatch构造器
public class CountDownLatch {
//定义一个自定义队列同步器
private final Sync sync;
/**
* 创建一个指定计数的构造器 ,初始化自定义队列同步器
*/
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
解释:当CountDownLatch初始化那么就初始化内部自定义同步器Sync。
CountDownLatch对外提供的方法
下面大部分采用了AQS的实现:具体可以去参考我的AQS源码解析。
https://www.jianshu.com/p/43c71be69f27
await方法
/**
* 让当前的线程对象一直处于等待的状态,直到计数器为0才停止等待。
* 如果中间产生了中断,那么就停止等待。
*/
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
解释:在共享模式下获取对象的状态。
countDown方法
/**
* 采用共享模式来释放锁,如果计数减为0了,那么就释放所有等待的线程,
*/
public void countDown() {
sync.releaseShared(1);
}
解释:在共享模式下释放对象的状态。
getCount方法
/**
* 获取当前计数值(采用原子的方式来获取当前的同步状态值)
*/
public long getCount() {
return sync.getCount();
}
解释:在共享模式下获取当前的同步状态值。
题外话:CountDownLatch的使用场景
当我们开启多个线程时,不知道什么时候多个线程才结束,可以通过工具CountDownLatch来判断等待多个线程执行完毕的结束点。
阅读总结
(1)CountDownLatch这个工具类采用同步队列器AQS共享锁来实现的。
(2)CountDownLatch释放同步对象,采用AQS的套路:死循环+cas乐观锁的方式来实现的。
(3)CountDownLatch需要注意的是,如果当前计数未CountDown()到0,那么调用await()的线程将处于阻塞无法执行的状态,但是其他的线程依然可以执行,从源码可以看出这个工具是采用共享式(共享=乐观)而非独占式(独占=悲观)来实现。
---------------------------该源码为jdk1.7版本的