CountDownLatch类是一个同步计数器,构造时传入int参数,该参数就是计数器的初始值,每调用一次countDown()方法,计数器减1,
计数器大于0 时,await()方法会阻塞程序继续执行
CountDownLatch如其所写,是一个倒计数的锁存器,当计数减至0时触发特定的事件。利用这种特性,可以让主线程等待子线程的结束。
1.一个模拟运动员比赛的例子
class Player implements Runnable { private int id; private CountDownLatch begin; private CountDownLatch end; public Player(int i, CountDownLatch begin, CountDownLatch end) { super(); this.id = i; this.begin = begin; this.end = end; } @Override public void run() { try { begin.await(); // 等待begin的状态为0 Thread.sleep((long) (Math.random() * 100)); // 随机分配时间,即运动员完成时间 System.out.println("Play" + id + " arrived."); } catch (InterruptedException e) { e.printStackTrace(); } finally { end.countDown(); // 使end状态减1,最终减至0 } } }
public class CountDownLatchTest { private static final int PLAYER_AMOUNT = 5; @Test public void RaceTest1( ) { // 对于每位运动员,CountDownLatch减1后即结束比赛 CountDownLatch begin = new CountDownLatch(1); // 对于整个比赛,所有运动员结束后才算结束 CountDownLatch end = new CountDownLatch(PLAYER_AMOUNT); Player[] plays = new Player[PLAYER_AMOUNT]; for (int i = 0; i < PLAYER_AMOUNT; i++) plays[i] = new Player(i + 1, begin, end); // 设置特定的线程池,大小为5 ExecutorService exe = Executors.newFixedThreadPool(PLAYER_AMOUNT); for (Player p : plays){ exe.execute(p); // 分配线程 } System.out.println("Race begins!"); begin.countDown(); try { end.await(); // 等待end状态变为0,即为比赛结束 } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println("Race ends!"); } exe.shutdown(); } }
2.健康检查
public class CheckHealthStartup { // List of service checkers private static List<BaseHealthChecker> _services; // This latch will be used to wait on private static CountDownLatch _latch; private CheckHealthStartup() { } private final static CheckHealthStartup INSTANCE = new CheckHealthStartup(); public static CheckHealthStartup getInstance() { return INSTANCE; } public static boolean checkExternalServices() throws Exception { // Initialize the latch with number of service checkers _latch = new CountDownLatch(2); _services = new ArrayList<BaseHealthChecker>(); // All add checker in lists _services.add(new NetworkHealthChecker(_latch)); _services.add(new DatabaseHealthChecker(_latch)); // Start service checkers using executor framework Executor executor = Executors.newFixedThreadPool(_services.size()); for (final BaseHealthChecker v : _services) { executor.execute(v); } // Now wait till all services are checked _latch.await(); // Services are file and now proceed startup for (final BaseHealthChecker v : _services) { if (!v.isServiceUp()) { return false; } } return true; } public static void main(String[] args) { boolean result = false; try { result = CheckHealthStartup.checkExternalServices(); } catch (Exception e) { e.printStackTrace(); } System.out.println("External services validation completed !! Result was :: " + result); } } class NetworkHealthChecker extends BaseHealthChecker { public NetworkHealthChecker(CountDownLatch latch) { super("Network Service", latch); } @Override public void verifyService() { System.out.println("Checking " + this.getServiceName()); try { Thread.sleep(7000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(this.getServiceName() + " is UP"); } } class DatabaseHealthChecker extends BaseHealthChecker { public DatabaseHealthChecker(CountDownLatch latch) { super("Database Service", latch); } @Override public void verifyService() { System.out.println("Checking " + this.getServiceName()); try { Thread.sleep(7000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(this.getServiceName() + " is UP"); } } abstract class BaseHealthChecker implements Runnable { private CountDownLatch _latch; private String _serviceName; private boolean _serviceUp; // Get latch object in constructor so that after completing the task, thread // can countDown() the latch public BaseHealthChecker(String serviceName, CountDownLatch latch) { super(); this._latch = latch; this._serviceName = serviceName; this._serviceUp = false; } @Override public void run() { try { verifyService(); _serviceUp = true; } catch (Throwable t) { t.printStackTrace(System.err); _serviceUp = false; } finally { if (_latch != null) { _latch.countDown(); } } } public String getServiceName() { return _serviceName; } public boolean isServiceUp() { return _serviceUp; } // This methos needs to be implemented by all specific service checker public abstract void verifyService(); }