JDK8 都快要出来了,在 JDK 5 中仍有许多好宝贝值得去挖掘。提到 JDK5 我们或许只知道它给了我们泛型,其实还有那个并发包 java.util.concurrent 却不那么引人注目,其实就是 NIO。
若是并发包是在某个 JDK 版本中单独奉上,反响就不同了,想想 JDK 6 似乎未带来多少改变--至少对于编程者来说没有明显感受。java.util.concurrent 包中的东西对于我们处理线程带来了很大的便利,例如线程池,线程同步,Future, Callable 等。
这里我记录一下 CountDownLatch 的使用,在此之前在处理
线程 A 等待线程 B,C,D 全部执行完后才继续执行 (比如要每个线程都访问一个 Web 服务,等所有的请求响应成功后进行结果处理)
这样场景的时候,我一般能想到的办法是,初始一个计数器,线程 B,C,D 各自初始化的时候,计数器加一,然后 A 线程等待,每个线程执行完后计数器减一,当计数器为 0 时表明所有任务执行完毕,就通知 A 可以开始运作起来。但这样的方案还是得小心的处理好同步的问题。
这可以用 JDK 5 java.util.concurrent.CountDownLatch,它的实现原理基本与上一致。也是需要任务在执行完毕后执行一下 countDown() 方式使得计数减一,当为 0 时,A 被唤醒。CountDown 就是倒计数,Latch 是门拴的意思,可以理解为某一线程等待所有它关注的线程全部出去(或进来)后把门拴上。
看个示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
package cc.unmi.test.concurrent; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; public class TestCountDownLatch { public static void main(String args[]) { final CountDownLatch latch = new CountDownLatch(1); Thread cacheService = new Thread(new Service("CacheService", 1000, latch)); Thread alertService = new Thread(new Service("AlertService", 1000, latch)); cacheService.start(); //separate thread will initialize CacheService alertService.start(); //another thread for AlertService initialization try{ latch.await(1000, TimeUnit.SECONDS); //main thread is waiting on CountDownLatch to finish System.out.println("All services are up, Application is starting now"); }catch(InterruptedException ie){ } } } class Service implements Runnable{ private final String name; private final int timeToStart; private final CountDownLatch latch; public Service(String name, int timeToStart, CountDownLatch latch){ this.name = name; this.timeToStart = timeToStart; this.latch = latch; } @Override public void run() { try { Thread.sleep(timeToStart); } catch (InterruptedException ex) { Logger.getLogger(Service.class.getName()).log(Level.SEVERE, null, ex); } System.out.println( name + " is Up"); latch.countDown(); //reduce count of CountDownLatch by 1 } } |
输出是:
AlertService is Up
CacheService is Up
All services are up, Application is starting now
PlayFramework 的 controller 中的 await 和这很相拟,它较喜欢用 Promise 这个词。
CountDownLatch 变成 0 后就不能重用了,如果想要能重用的就使用 java.util.concurrent.CyclicBarrier 吧,它用到了 ReentrantLock 的概念。
参考:1. What is CountDownLatch in Java - Concurrency Example Tutorial
2. What is CyclicBarrier Example in Java 5 – Concurrency Tutorial
本文链接 https://yanbin.blog/countdownlatch-threads/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
JDK5的线程相关的API非常值得研究。