Java中的CountDownLatch

时间:2020-01-09 10:35:10  来源:igfitidea点击:

Java中的CountDownLatch是一种同步辅助工具,它允许一个或者多个线程等待在其他线程中执行的一组操作完成。

CountDownLatch类是在Java 1.5中添加的,它是Java中java.util.concurrent包的一部分。

CountDownLatch如何在Java中工作

CountDownLatch使用给定的计数初始化,然后有两种方法await()和countDown()阻塞并释放线程。

await()–使当前线程等待,直到锁存器计数到零为止。

countDown()–减少锁存器的计数,如果计数达到零,则释放所有等待线程。

顾名思义,CountDownLatch可以看作是固定闩锁,以便在调用await方法并等待从给定计数开始倒数到零时阻止当前线程。当计数达到零时,打开锁存器以继续执行线程。

Java CountDownLatch构造函数

CountDownLatch(int count)–构造一个使用给定计数初始化的CountDownLatch。这里count指定通过在每个事件之后递减计数来使计数为零所必须发生的事件数。

Java CountDownLatch方法

除了上面已经显示的await()和countDown()方法之外,CountDownLatch类中还有一些其他方法。

await(长超时,TimeUnit单位)–导致当前线程等待,直到锁存器计数到零为止,除非该线程被中断或者经过了指定的等待时间。

getCount()–返回当前计数。

CountDownLatch Java示例

假设我们有一个场景,其中有一组线程正在执行某些任务,但是我们希望这些线程仅在主线程完成一些设置工作之后才能启动。一旦线程集开始,主线程应该等待并仅在线程集完成任务后才继续。在这种情况下,我们可以使用两个倒数闩锁

First CountDownLatch用于防止任何线程在主线程完成设置任务之前开始其操作。

第二个CountDownLatch用作完成信号,使线程集等待所有线程完成。这给了主线程一个继续前进的信号。

import java.util.concurrent.CountDownLatch;

public class CLDemo {
  public static void main(String[] args) {
    CountDownLatch startLatch = new CountDownLatch(1);
    CountDownLatch doneLatch = new CountDownLatch(3);
    for(int i = 0; i < 3; i++) {
      new Thread(new Worker(startLatch, doneLatch)).start();;
    }
    try {
      Thread.sleep(500);
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    System.out.println("Main thread - Doing setup");
    //Signal from main thread for other threads to start
    startLatch.countDown();
    try {
      // Main thread waiting for other threads to finish
      doneLatch.await();
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    System.out.println("Main thread - Proceed after all the threads are done");
  }
}

class Worker implements Runnable{
  private final CountDownLatch startLatch;
  private final CountDownLatch doneLatch;
  Worker(CountDownLatch startLatch, CountDownLatch doneLatch) {
    this.startLatch = startLatch;
    this.doneLatch = doneLatch;
  }
  @Override
  public void run() {
    try {
      System.out.println("Calling await ...");
      // don't start until set up is done
      startLatch.await();
      System.out.println("Doing processing " + Thread.currentThread().getName());
      doneLatch.countDown();
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}

输出:

Calling await ...
Calling await ...
Calling await ...
Main thread - Doing setup
Doing processing Thread-0
Doing processing Thread-2
Doing processing Thread-1
Main thread - Proceed after all the threads are done

从输出中可以看到,三个线程已启动,所有三个线程也开始执行run方法,但被对startLatch.await()的调用阻塞了;这样可以确保线程在设置完成后才开始处理。一旦startLatch.countDown();在主线程中被调用时,线程开始处理。

现在,主线程由于调用doneLatch.await()而等待。由于doneLatch用3的计数初始化,因此主线程被阻塞,直到countDown()方法被调用3次为止。一旦计数达到0,主线程便继续执行。

Java中的CountDownLatch不可重用

Java中CountDownLatch的一个重要点是,它不能像其他可重用的同步辅助工具CyclicBarrier那样重用。一旦计数达到零,就无法重置。一旦倒计时达到零,对await()方法的调用将不会阻塞任何线程,因此请确保初始化CountDownLatch计数与调用countDown()方法的次数之间的同步。

在前面的示例中,如果我们创建6个线程而不是3个线程,但让doneLatch中的计数保持为3.

public class CLDemo {
  public static void main(String[] args) {
    CountDownLatch startLatch = new CountDownLatch(1);
    CountDownLatch doneLatch = new CountDownLatch(3);
    for(int i = 0; i < 6; i++) {
      new Thread(new Worker(startLatch, doneLatch)).start();;
    }
    try {
      Thread.sleep(500);
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    System.out.println("Main thread - Doing setup");
    //Signal from main thread for other threads to start
    startLatch.countDown();
    try {
      // Main thread waiting for other threads to finish
      doneLatch.await();
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    System.out.println("Main thread - Proceed after all the threads are done");
  }
}

class Worker implements Runnable{
  private final CountDownLatch startLatch;
  private final CountDownLatch doneLatch;
  Worker(CountDownLatch startLatch, CountDownLatch doneLatch) {
    this.startLatch = startLatch;
    this.doneLatch = doneLatch;
  }
  @Override
  public void run() {
    try {
      System.out.println("Calling await ...");
      // don't start until set up is done
      startLatch.await();
      System.out.println("Doing processing " + Thread.currentThread().getName());
      doneLatch.countDown();
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}

输出:

Calling await ...
Calling await ...
Calling await ...
Calling await ...
Calling await ...
Calling await ...
Main thread - Doing setup
Doing processing Thread-1
Doing processing Thread-4
Doing processing Thread-3
Doing processing Thread-2
Doing processing Thread-0
Main thread - Proceed after all the threads are done
Doing processing Thread-5

从输出中可以看到,一旦倒数从3变为零,主线程就可以随时开始处理。