在java并发中,控制共享变量的访问非常重要,有时候我们也想控制并发线程的执行顺序,比如:等待所有线程都执行完毕之后再执行另外的线程,或者等所有线程都准备好了才开始所有线程的执行等。

这个时候我们就可以使用到CountDownLatch。简单点讲,CountDownLatch存有一个放在QueuedSynchronizer中的计数器。当调用countdown() 方法时,该计数器将会减一。然后再调用await()来等待计数器归零。

一、主线程等待子线程全都结束之后再开始运行

这里我们定义子线程类,在子线程类里面,我们传入一个CountDownLatch用来计数,然后在子线程结束之前,调用该CountDownLatch的countDown方法。最后在主线程中调用await()方法来等待子线程结束执行。

  1. /**
  2. * 功能描述 Task
  3. *
  4. * @author h00518386
  5. * @since 2020-06-11
  6. */
  7. public class MainThreadWaitUsage implements Runnable {
  8. private List<String> outputScraper;
  9. private CountDownLatch countDownLatch;
  10. public MainThreadWaitUsage(List<String> outputScraper, CountDownLatch countDownLatch) {
  11. this.outputScraper = outputScraper;
  12. this.countDownLatch = countDownLatch;
  13. }
  14. @Override
  15. public void run() {
  16. outputScraper.add("Counted down");
  17. countDownLatch.countDown();
  18. }
  19. }

测试代码:

  1. public class TestCountDownLatch {
  2. @Test
  3. public void testCountDownLatch() throws InterruptedException {
  4. List<String> outputScraper = Collections.synchronizedList(new ArrayList<>());
  5. CountDownLatch countDownLatch = new CountDownLatch(5);
  6. List<Thread> workers = Stream.generate(() -> new Thread(new MainThreadWaitUsage(outputScraper, countDownLatch)))
  7. .limit(5)
  8. .collect(toList());
  9. workers.forEach(Thread::start);
  10. countDownLatch.await();
  11. outputScraper.add("Latch released");
  12. System.out.println(outputScraper.toString());
  13. }
  14. }

补充一点知识:java8生成流

  1. Random seed = new Random();
  2. Supplier<Integer> random = seed::nextInt;
  3. Stream.generate(random).limit(10).forEach(System.out::println);
  4. //Another way
  5. IntStream.generate(() -> (int) (System.nanoTime() % 100)).
  6. limit(10).forEach(System.out::println);

二、停止CountdownLatch的await

如果我们调用await()方法,该方法将会等待一直到count=0才结束。但是如果在线程执行过程中出现了异常,可能导致countdown方法执行不了。那么await()方法可能会出现无限等待的情况。

这个时候我们可以使用:

  1. public boolean await(long timeout, TimeUnit unit)
  2. throws InterruptedException {
  3. return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
  4. }