在日常开发中经常会遇到需要在主线任务中开启多个线程,并等到所有子线程完成工作之后再进行汇总的情形,这种情况下可以考虑使用CountDownLatch这个工具类实现功能,下面是使用CountDownLatch的一个示例:
package xiancheng.Lock;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchTest {
public static void main(String[] args) {
//创建一个CountDownLatch实例,计数器记为3
final CountDownLatch latch=new CountDownLatch(3);
new Thread(){
public void run(){
try{
System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
Thread.sleep(3000);
System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
latch.countDown();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}.start();
new Thread(){
public void run(){
try{
System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
Thread.sleep(3000);
System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
latch.countDown();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}.start();
new Thread(){
public void run(){
try{
System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
Thread.sleep(3000);
System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
latch.countDown();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}.start();
try {
System.out.println("等待3个子线程执行完毕...");
latch.await();
System.out.println("3个子线程已经执行完毕");
System.out.println("继续执行主线程");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
子线程Thread-0正在执行
子线程Thread-1正在执行
等待3个子线程执行完毕...
子线程Thread-2正在执行
子线程Thread-0执行完毕
子线程Thread-1执行完毕
子线程Thread-2执行完毕
3个子线程已经执行完毕
继续执行主线程
以上示例很清晰地反映了CountDownLatch类的使用方法,上述代码其实还不够简洁,如果使用线程池代码会更优雅。从CountDownLatch这个类名可以推测,该类内部应该是有一个计数器,并且这个计数器是递减的,实际上CountDownLatch就是通过前面所提到的AQS组件实现的,下面来看看该类的源代码:
public class CountDownLatch {
//实现AQS组件的内部类,可以看出CountDownLatch是通过AQS实现的
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
//设置计数器实际上是将值赋给了AQS状态变量state
Sync(int count) {
setState(count);
}
//获取状态变量state的值
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
//循环进行CAS,直到当前线程完成CAS减去1操作
for (;;) {
int c = getState();
//当前状态值为0则直接返回
if (c == 0)
return false;
int nextc = c-1;
//使用CAS让计数器值减去1
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
private final Sync sync;
//构造方法调用Sync类的构造函数
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
//调用该方法后线程会被阻塞,直到发生下列情况之一才会返回:
1所有线程调用countdown方法,计数器的值变为0
2设置的时间到了,超时返回
3其他线程调用了当前线程的interrupt()方法中断了当前线程
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
//
public void countDown() {
调用sync类的方法,使state的值减去1
sync.releaseShared(1);
}
//调用该方法获得state的值,一般在测试的时候使用
public long getCount() {
return sync.getCount();
}
public String toString() {
return super.toString() + "[Count = " + sync.getCount() + "]";
}
}
总结一下CountDownLatch的工作原理:CountDownLatch使使用AQS实现的,使用AQS的state变量来存放计数器的值。在调用CountDownLatch的构造函数时,会调用内部类Sync的构造函数将值赋给state变量,当多个线程调用countdown方法时实际是使用CAS递减state变量的值;当线程调用await方法后当前线程会被放入AQS阻塞队列等待计数器为0时返回,即所有线程都调用了countdown方法时。最后,当计数器的值变为0时,当前线程还会调用AQS的doReleasedShared()方法激活调用await()方法而被阻塞的线程。