CountDownLatch 是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行.
CountDownLatch 是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。
主要方法有:
- CountDownLatch (int count),构造一个用给定计数器初始化的CountDownLatch。构造器中的计数值(count)实际上就是闭锁需要等待的线程数量。这个值只能被设置一次,而且CountDownLatch没有提供任何机制去重新设置这个计数值。
- void await (),使当前线程在锁存器倒计数至0之前一直等待,除非线程被中断。
- boolean await (long timeout, TimeUnit unit),使当前线程在锁存器,倒计数至0之前一直等待,除非线程被中断或超出了指定的等待时间。
- void countDwon (),递减锁存器的计数,如果计数达到0,则释放所有等待的线程。
- long getCount (),返回当前计数。
使用场景:
在一些应用场景中,需要等待某个条件达到要求后才能做后面的事情;同时当线程都完成后也会触发事件,以便进行后面操作。
CountDownLatch的countDown() 和 await(),前者主要倒数一次,后者是等待倒数到0,如果没有到0,就只有阻塞等待了。
- 应用场景1:开5个多线程去下载,当5个线程都执行完了,才算下载成功。
- 应用场景2:当用户多文件上传时,可以采用多线程上传,当多个文件都上传成功时,才算真正的上传成功。
- 实现最大的并行性:有时我们想同时启动多个线程,实现最大程度的并行性。例如,我们想测试一个单例类。如果我们创建一个初始计数为1的CountDownLatch,并让所有线程都在这个锁上等待,那么我们可以很轻松地完成测试。我们只需调用 一次countDown()方法就可以让所有的等待线程同时恢复执行。
- 开始执行前等待n个线程完成各自任务:例如应用程序启动类要确保在处理用户请求前,所有N个外部系统已经启动和运行了。
- 死锁检测:一个非常方便的使用场景是,你可以使用n个线程访问共享资源,在每次测试阶段的线程数目是不同的,并尝试产生死锁。
1 public class Demo { 2 3 public static void main(String[] args) throws Exception { 4 5 CountDownLatch latch = new CountDownLatch(3); 6 Worker worker1 = new Worker("xiao ming", latch); 7 Worker worker2 = new Worker("xiao hong", latch); 8 Worker worker3 = new Worker("xiao wang", latch); 9 10 worker1.start(); 11 worker2.start(); 12 worker3.start(); 13 14 latch.await(); 15 16 System.out.println("Main Thread End."); 17 } 18 19 static class Worker extends Thread { 20 21 private String workerName; 22 private CountDownLatch latch; 23 24 public Worker(String workerName, CountDownLatch latch) { 25 26 this.workerName = workerName; 27 this.latch = latch; 28 } 29 30 @Override 31 public void run() { 32 33 try { 34 System.out.println("Worker:" + workerName + " is begin."); 35 Thread.sleep(1000L); 36 System.out.println("Worker:" + workerName + " is end."); 37 } catch (Exception e) { 38 e.printStackTrace(); 39 } 40 latch.countDown(); 41 } 42 } 43 }
输出:
1 Worker:xiao ming is begin. 2 Worker:xiao hong is begin. 3 Worker:xiao wang is begin. 4 Worker:xiao ming is end. 5 Worker:xiao wang is end. 6 Worker:xiao hong is end. 7 Main Thread End.
CountDownLatch 的实现原理:
CountDownLatch 的核心实现机制利用 AbstractQueuedSynchronizer 简称“AQS”的 state状态来实现Count的阻塞机制。
1 public class CountDownLattch { 2 3 /** 4 *CountDownLatch 的核心实现机制利用 AbstractQueuedSynchronizer 简称“AQS”的 state状态来实现Count的阻塞机制 5 */ 6 7 private static final class Sync extends AbstractQueuedSynchronizer { 8 9 Sync(int count) { 10 setState(count); 11 } 12 13 int getCount() { 14 return getState(); 15 } 16 17 protected int tryAcquireShared(int acquires) { 18 return (getState() == 0) ? 1 : -1; 19 } 20 21 protected boolean tryReleaseShared(int releases) { 22 // 覆盖"AQS"的释放状态方式,实现自己的逻辑,来消减count的线程数 23 for(;;) { 24 25 int c = getState(); 26 if (c == 0) 27 return false; 28 29 int nextc = c - 1; 30 if (compareAndSetState(c, nextc)) 31 return nextc == 0; 32 } 33 } 34 35 } 36 37 private final Sync sync; 38 // 利用"AQS"的state状态,来标示线程的count的数量 39 40 public CountDownLat(int count) { 41 this.sync = new Sync(count); 42 } 43 44 // 利用"AQS"获得一个共享模式下的完成状态 45 public void await() throws InterruptedException { 46 sync.acquireSharedInterruptibly(1); 47 } 48 49 // 利用"AQS"获得一个共享模式下的完成状态,超出了指定的等待时间 50 public void await(int timeout, TimeUnit unit) throws InterruptedException { 51 sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); 52 } 53 54 // 利用"AQS"在共享模式下释放状态也就是数字减一 55 public void countDown() { 56 sync.releaseShared(1); 57 } 58 59 // 调用"AQS"返回当前计数 60 public long getCount() { 61 return sync.getCount(); 62 } 63 64 ... 65 }