CountDownLatch

65 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第18天,点击查看活动详情

  • countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。
  • 是通过一个 state(相当于计数器)的东西来实现的,计数器的初始值是 线程的数量或者任务的数量
  • 每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。
  • CountDownLatch的方便之处在于,你可以在一个线程中使用, 也可以在多个线程上使用,一切只依据状态值,这样便不会受限于任何的场景。

使用场景一

需求

  • 可能刚从数据库读取了一批数据
  • 利用并发处理这批数据
  • 当所有的数据处理完成后,再去执行后面的操作

解决方案

  • 第一种:可以利用 join 的方法,但是在线程池中,比较麻烦。

  • 第二种:利用线程池的awaitTermination,阻塞一段时间。

    • 当使用awaitTermination时,主线程会处于一种等待的状态,等待线程池中所有的线程都运行完毕后才继续运行。
  • 第三种:利用CountDownLatch,每当任务完成一个,就计数器减一。

场景一使用如下(每当任务完成一个,就计数器减一):

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 0; i < 6; i++) {
             new Thread(() ->{
                System.out.println("\t\t" + Thread.currentThread().getName() + "处理完毕~~~");
                countDownLatch.countDown();
                System.out.println("非调用者线程-" + Thread.currentThread().getName() + "-还可以干点其他事");
             }, Country.forEach_Country(i + 1).getCountryName()).start();
        }

        countDownLatch.await();
        System.out.println("-----------------------------");
        System.out.println("\t 所有任务都已经处理完毕,可以往后执行了!");

    }
}

场景二: 因为countdown只会阻塞调用者,其它线程干完任务就可以干其他事。这里的调用者线程就是main线程。

  • 多个线程协同工作
  • 多个线程需要等待其他线程的工作之后,再进行其后续工作。
  • 被唤醒后继续执行其他操作
    public static void main(String[] args) throws InterruptedException {
        final CountDownLatch latch = new CountDownLatch(1);
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + " Do some initial working.");
            try {
                Thread.sleep(1000);
                latch.await();
                System.out.println(Thread.currentThread().getName() + " Do other working.");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + " Do some initial working.");
            try {
                Thread.sleep(1000);
                latch.await();
                System.out.println(Thread.currentThread().getName() + " Do other working.");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(() -> {
            System.out.println("asyn prepare for some data.");
            try {
                Thread.sleep(2000);
                System.out.println("Data prepare for done.");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                latch.countDown();
            }
        }).start();

    }

常用API

构造方法只有一个

  • CountDownLatch(int count) :构造一个以给定计数

实例方法

  • public void await()

    • 当前线程等到锁存器计数到零
    • 可以被 打断
  • public boolean await(long timeout,TimeUnit unit)

    • 等待一段时间
    • timeout - 等待的最长时间 , unit - timeout参数的时间单位
    • 如果 指定的等待时间过去,则返回值false
    • 如果 计数达到零,则方法返回值为true
  • public void countDown()

    • 减少锁存器的计数, 如果计数达到零,释放所有等待的线程
  • public long getCount()

    • 返回当前计数