CountDownLatch 是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程执行完后再执行。
- CountDownLatch 在 JDK 1.5 被引入。
- 存在于 java.util.concurrent 包下。
- 应用程序的主线程希望在负责启动框架服务的线程已经启动所有框架服务之后执行
最重要的方法是countDown()和await(),前者主要是倒数一次,后者是等待倒数到0,如果没有到达0,就只有阻塞等待了。
一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。
之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier。
CountDownLatch 是一个通用同步工具,它有很多用途。将计数 1 初始化的 CountDownLatch 用作一个简单的开/关锁存器,
或入口:在通过调用 countDown() 的线程打开入口前,所有调用 await 的线程都一直在入口处等待。
用 N 初始化的 CountDownLatch 可以使一个线程在 N 个线程完成某项操作之前一直等待,或者使其在某项操作完成 N 次之前一直等待。
CountDownLatch 的一个有用特性是,它不要求调用 countDown 方法的线程等到计数到达零时才继续,
而在所有线程都能通过之前,它只是阻止任何线程继续通过一个 await。
此例很形象的描述了countDown的执行过程
public class TestCountDownLatch {
public static void main(String[] args) throws InterruptedException {
// 开始的倒数锁
final CountDownLatch begin = new CountDownLatch(1);
// 结束的倒数锁
final CountDownLatch end = new CountDownLatch(10);
// 十名选手
final ExecutorService exec = Executors.newFixedThreadPool(10);
for (int index = 0; index < 10; index++) {
final int NO = index + 1;
Runnable run = new Runnable() {
public void run() {
try {
begin.await();//一直阻塞
Thread.sleep((long) (Math.random() * 10000));
System.out.println("No." + NO + " arrived");
} catch (InterruptedException e) {
} finally {
end.countDown();
}
}
};
exec.submit(run);
}
System.out.println("Game Start");
begin.countDown();
end.await();
System.out.println("Game Over");
exec.shutdown();
}
}
//执行结果为:
Game Start
No.10 arrived
No.2 arrived
No.7 arrived
No.4 arrived
No.9 arrived
No.3 arrived
No.5 arrived
No.6 arrived
No.8 arrived
No.1 arrived
Game Over
应用一,zookeeper 创建
public class ConnectingExample {
private static final Logger LOG = LoggerFactory.getLogger(ConnectingExample.class);
private static final int SESSION_TIMEOUT = 5000;
public ZooKeeper connect(String hosts) throws IOException, InterruptedException {
final CountDownLatch signal = new CountDownLatch(1);
ZooKeeper zk = new ZooKeeper(hosts, SESSION_TIMEOUT, event -> {
if (event.getState() == Watcher.Event.KeeperState.SyncConnected) {
signal.countDown();
}
});
signal.await();
return zk;
}
public static void main(String[] args) throws IOException, InterruptedException {
ConnectingExample example = new ConnectingExample();
ZooKeeper zk = example.connect("192.168.25.130:2181");//安装服务IP,端口号
LOG.info("ZK state: {}", zk.getState());
zk.close();
}
}
对于集群模式,另一种写法
public class ZookeeperSession {
private static CountDownLatch connectedSemaphore = new CountDownLatch(1);
private ZooKeeper zooKeeper;
private ZookeeperSession(){
// 去连接zookeeper server, 创建会话的时候,是异步进行的
// 所有要给一个监听器,说告诉我们什么时候是真正的完成跟zookeeper server 连接
try {
this.zooKeeper = new ZooKeeper("192.168.43.10:2181,192.168.43.11:2181,192.168.43.12:2181",
5000,
new ZookeeperWather());
log.info("### zookeeper server 连接成功, state={} ###", zooKeeper.getState());
connectedSemaphore.await();
} catch (Exception e) {
e.printStackTrace();
}
}
private class ZookeeperWather implements Watcher {
@Override
public void process(WatchedEvent watchedEvent) {
if (Event.KeeperState.SyncConnected == watchedEvent.getState()) {
connectedSemaphore.countDown();
}
}
}
private static class SingleTon {
private static ZookeeperSession instance = new ZookeeperSession();
public static ZookeeperSession getInstance() {
return SingleTon.instance;
}
}
public static ZookeeperSession getInstance() {
return SingleTon.getInstance();
}
public static void init() {
getInstance();
}
}