CountDownLatch(闭锁)是一个同步协助类,允许一个或多个线程等待,直到其他线程完成操作集
    使用一个计算值count 初始化,await方法阻塞到当前计数值,由于countDown方法调用达到0,
    count达到0之后所有等待的线程会被释放
    并且随后对await方法的调用会立即返回,一次性的,count不会充值
    CyclicBarrier,会重置
    1648390021(1).png
    CountDownLatch的使用

    1. /**
    2. * Constructs a {@code CountDownLatch} initialized with the given count.
    3. *
    4. * @param count the number of times {@link #countDown} must be invoked
    5. * before threads can pass through {@link #await}
    6. * @throws IllegalArgumentException if {@code count} is negative
    7. */
    8. public CountDownLatch(int count) {
    9. if (count < 0) throw new IllegalArgumentException("count < 0");
    10. this.sync = new Sync(count);
    11. }

    常用方法

    1. // 调用 await() 方法的线程会被挂起,它会等待直到 count 值为 0 才继续执行
    2. public void await() throws InterruptedException { };
    3. // 和 await() 类似,若等待 timeout 时长后,count 值还是没有变为 0,不再等待,继续执行
    4. public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };
    5. // 会将 count 减 1,直至为 0
    6. public void countDown() { };

    CountDownLatch应用场景
    CountDownLatch一般用作多线程倒计时计数器,强制它们等待其他一组(CountDownLatch的初始化决定)任务执行完成。
    CountDownLatch的两种使用场景:

    • 场景1:让多个线程等待
    • 场景2:让单个线程等待。
    1. public class CountDownLatchTest {
    2. public static void main(String[] args) throws InterruptedException {
    3. CountDownLatch countDownLatch = new CountDownLatch(1);
    4. for (int i = 0; i < 5; i++) {
    5. new Thread(() -> {
    6. try {
    7. //准备完毕……运动员都阻塞在这,等待号令
    8. countDownLatch.await();
    9. String parter = "【" + Thread.currentThread().getName() + "】";
    10. System.out.println(parter + "开始执行……");
    11. } catch (InterruptedException e) {
    12. e.printStackTrace();
    13. }
    14. }).start();
    15. }
    16. Thread.sleep(2000);// 裁判准备发令
    17. countDownLatch.countDown();// 发令枪:执行发令
    18. }
    19. }
    1. public class CountDownLatchTest2 {
    2. public static void main(String[] args) throws Exception {
    3. CountDownLatch countDownLatch = new CountDownLatch(5);
    4. for (int i = 0; i < 5; i++) {
    5. final int index = i;
    6. new Thread(() -> {
    7. try {
    8. Thread.sleep(1000 + ThreadLocalRandom.current().nextInt(1000));
    9. System.out.println(Thread.currentThread().getName()+" finish task" + index );
    10. countDownLatch.countDown();
    11. } catch (InterruptedException e) {
    12. e.printStackTrace();
    13. }
    14. }).start();
    15. }
    16. // 主线程在阻塞,当计数器==0,就唤醒主线程往下执行。
    17. countDownLatch.await();
    18. System.out.println("主线程:在所有任务运行完成后,进行结果汇总");
    19. }
    20. }

    CountDownLatch实现原理
    底层基于 AbstractQueuedSynchronizer 实现,CountDownLatch 构造函数中指定的count直接赋给AQS的state;每次countDown()则都是release(1)减1,最后减到0时unpark阻塞线程;这一步是由最后一个执行countdown方法的线程执行的。
    而调用await()方法时,当前线程就会判断state属性是否为0,如果为0,则继续往下执行,如果不为0,则使当前线程进入等待状态,直到某个线程将state属性置为0,其就会唤醒在await()方法中等待的线程。

    CountDownLatch与Thread.join的区别

    • CountDownLatch的作用就是允许一个或多个线程等待其他线程完成操作,看起来有点类似join() 方法,但其提供了比 join() 更加灵活的API。
    • CountDownLatch可以手动控制在n个线程里调用n次countDown()方法使计数器进行减一操作,也可以在一个线程里调用n次执行减一操作。
    • 而 join() 的实现原理是不停检查join线程是否存活,如果 join 线程存活则让当前线程永远等待。所以两者之间相对来说还是CountDownLatch使用起来较为灵活。

    CountDownLatch与CyclicBarrier的区别
    CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:

    1. CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次
    2. CyclicBarrier还提供getNumberWaiting(可以获得CyclicBarrier阻塞的线程数量)、isBroken(用来知道阻塞的线程是否被中断)等方法。
    3. CountDownLatch会阻塞主线程,CyclicBarrier不会阻塞主线程,只会阻塞子线程。
    4. CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同。CountDownLatch一般用于一个或多个线程,等待其他线程执行完任务后,再执行。CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行。
    5. CyclicBarrier 还可以提供一个 barrierAction,合并多线程计算结果。
    6. CyclicBarrier是通过ReentrantLock的”独占锁”和Conditon来实现一组线程的阻塞唤醒的,而CountDownLatch则是通过AQS的“共享锁”实现