概述 通过使用CountDownLatch,可以阻塞线程,直到其他线程完成给定的任务。 CountDownLatch有一个计数器字段,该值与我们想要处理的线程数相同。然后,可以在每个线程完成后调用countdown(),保证调用await()的依赖线程将阻塞,直到工作线程完成。等待线程池完成 让我们通过创建一个Worker并使用CountDownLatch字段在完成时发出信号来尝试这种模式:publicclassWorkerimplementsRunnable{privateListStringoutputSprivateCountDownLatchcountDownLpublicWorker(ListStringoutputScraper,CountDownLatchcountDownLatch){this。outputScraperoutputSthis。countDownLatchcountDownL}Overridepublicvoidrun(){doSomeWork();outputScraper。add(Counteddown);countDownLatch。countDown();}} 创建一个测试,验证可以获得CountDownLatch来等待Worker实例完成:TestpublicvoidwhenParallelProcessingthenMainThreadWillBlockUntilCompletion()throwsInterruptedException{ListStringoutputScraperCollections。synchronizedList(newArrayList());CountDownLatchcountDownLatchnewCountDownLatch(5);ListThreadworkersStream。generate(()newThread(newWorker(outputScraper,countDownLatch)))。limit(5)。collect(toList());workers。forEach(Thread::start);countDownLatch。await();outputScraper。add(Latchreleased);assertThat(outputScraper)。containsExactly(Counteddown,Counteddown,Counteddown,Counteddown,Counteddown,Latchreleased);}等待同时开始的线程池 如果启动了数千个线程,那么很可能许多前面的线程在我们对后面的线程调用start()之前就已经完成了处理。这可能会使尝试和再现并发问题变得困难,因为我们无法使所有线程并行运行。 为了解决这个问题,可以使用CountdownLatch阻塞每个子线程,直到所有其他子线程都开始。publicclassWaitingWorkerimplementsRunnable{privateListStringoutputSprivateCountDownLatchreadyThreadCprivateCountDownLatchcallingThreadBprivateCountDownLatchcompletedThreadCpublicWaitingWorker(ListStringoutputScraper,CountDownLatchreadyThreadCounter,CountDownLatchcallingThreadBlocker,CountDownLatchcompletedThreadCounter){this。outputScraperoutputSthis。readyThreadCounterreadyThreadCthis。callingThreadBlockercallingThreadBthis。completedThreadCountercompletedThreadC}Overridepublicvoidrun(){readyThreadCounter。countDown();try{callingThreadBlocker。await();doSomeWork();outputScraper。add(Counteddown);}catch(InterruptedExceptione){e。printStackTrace();}finally{completedThreadCounter。countDown();}}} 修改测试用例,使其阻塞直到所有Workers都开始,取消阻塞Workers,然后阻塞直到Workers完成:TestpublicvoidwhenDoingLotsOfThreadsInParallelthenStartThemAtTheSameTime()throwsInterruptedException{ListStringoutputScraperCollections。synchronizedList(newArrayList());CountDownLatchreadyThreadCounternewCountDownLatch(5);CountDownLatchcallingThreadBlockernewCountDownLatch(1);CountDownLatchcompletedThreadCounternewCountDownLatch(5);ListThreadworkersStream。generate(()newThread(newWaitingWorker(outputScraper,readyThreadCounter,callingThreadBlocker,completedThreadCounter)))。limit(5)。collect(toList());workers。forEach(Thread::start);readyThreadCounter。await();outputScraper。add(Workersready);callingThreadBlocker。countDown();completedThreadCounter。await();outputScraper。add(Workerscomplete);assertThat(outputScraper)。containsExactly(Workersready,Counteddown,Counteddown,Counteddown,Counteddown,Counteddown,Workerscomplete);} 这种模式对于尝试重现并发错误非常有用,因为它可以用来迫使数千个线程尝试并行执行一些逻辑。提前终止CountdownLatch 有时,可能会遇到这样一种情况,即Workers在倒计时之前错误地终止。这可能导致永远不会达到零,await()永远不会终止:Overridepublicvoidrun(){if(true){thrownewRuntimeException(Ohdear,ImaBrokenWorker);}countDownLatch。countDown();outputScraper。add(Counteddown);} 为了解决这个问题,await()的调用可添加一个超时参数。booleancompletedcountDownLatch。await(3L,TimeUnit。SECONDS);assertThat(completed)。isFalse();结论 CountDownLatch用来阻塞线程,直到其他线程完成一些处理的业务场景。