跟着ZooKeeper学Java——CountDownLatch和Join的使用

在阅读ZooKeeper的源码时,看到这么一个片段,在单机模式启动的时候,会调用下面的方法,根据zoo.cfg的配置启动单机版本的服务器:

public void runFromConfig(ServerConfig config) throws IOException {
    //1 创建ZooKeeper服务器
    final ZooKeeperServer zkServer = new ZooKeeperServer();
    final CountDownLatch shutdownLatch = new CountDownLatch(1);
    zkServer.registerServerShutdownHandler(
                    new ZooKeeperServerShutdownHandler(shutdownLatch));
    ...
    //2 创建ZooKeeper的NIO线程
    cnxnFactory = ServerCnxnFactory.createFactory();
    cnxnFactory.configure(config.getClientPortAddress(),
            config.getMaxClientCnxns());
    cnxnFactory.startup(zkServer);
    
    // 3 调用shutdownLatch阻塞,服务器在新线程正常处理请求
    shutdownLatch.await();
    // 4 如果有错误,直接调用shutdown
    shutdown();
    // 5 执行cnx.join()等待NIO线程关闭
    cnxnFactory.join();
    if (zkServer.canShutdown()) {
        zkServer.shutdown(true);
    }
}
...
protected void shutdown() {
    if (cnxnFactory != null) {
        cnxnFactory.shutdown();
    }
}

其中比较有意思的两个地方:

1 CountDownLatch的使用

开启NIO新线程接收客户端的请求,服务端的主线程直接利用countdownlatch挂起。这个CountDownLatch之前有说过,就是个多线程的计数器。

详细内容参考文章——Java计数器之CountDownLatch、CyclicBarrier、Semaphore

2 join的作用

join的作用就是主线程遇到A.join()之后,就会挂起;登到A结束后,才会继续执行。可以理解为主线程调用了A的wait方法...

下面有个Join的小例子:

public class JoinTest {
    public static void main(String[] args) throws InterruptedException {
        Thread A = new Thread(()->{
            System.out.println("进入线程A");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("A:线程A结束");
        });
        A.start();

        System.out.println("Main:等待线程A结束");
        A.join();
        System.out.println("Main:线程A已经结束,退出");
    }
}

输出内容为:

Main:等待线程A结束
进入线程A
A:线程A结束
Main:线程A已经结束,退出

可以看到,Main线程在A.join()的地方挂起了。等A结束后,才继续执行。

ZooKeeper这样的设计,使服务器在关闭前,确保NIO线程正确关闭,避免NIO资源的未释放。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏微信公众号:Java团长

Java开发进销存管理系统(二)

在进行进销存系统的设计和编码之前,首先设计一个自己的底层框架,这个底层框架在之后可以作为其它具体项目开发的一个基础,从而不必每次开发项目时,都去做很多重复的工作...

85440
来自专栏架构说

进程fork函数

验证1 fork会重新拷贝父进程的一份资源 例如 环境变量 公共变量 代码地址: https://code.csdn.net/snippets/1697496...

39880
来自专栏博岩Java大讲堂

Java集合--阻塞队列(引言)

364120
来自专栏Android干货

Android Studio 之 项目瘦身、代码检查

35750
来自专栏IT技术精选文摘

RMI原理及实现

1 简介 RMI是远程方法调用的简称,像其名称暗示的那样,它能够帮助我们查找并执行远程对象的方法。通俗地说,远程调用就象将一个class放在A机器上,然后在B机...

20280
来自专栏好好学java的技术栈

bat等大公司常考java多线程面试题

简而言之,进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程.进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切...

10240
来自专栏前端黑板报

再谈异步

小亲冈 爱屋吉屋 前端开发工程师 按顺序完成异步操作 实际开发中,经常遇到一组异步操作,需要按照顺序完成。比如,展示页面中有上中下三个部分,每一部分通过一个接口...

21050
来自专栏Java架构沉思录

Java面试通关宝典(二)

前言 在之前的文章《Java面试通关宝典(一)》中,沉思君为大家介绍了几道常见的面试题与参考答案,有些题目还附有延伸问题,如果不清楚这些题目的思路,可以申请进...

29570
来自专栏爱撒谎的男孩

struts核心配置文件详解(result配置)

<package name="Login" extends="struts-default" namespace="/"> ​ <action name...

36650
来自专栏前端侠2.0

asp。net5的依赖注入 原

昨天读asp.net5的doc,看到了configure的配置时,提到在controller中访问配置就是通过依赖注入的。asp.net5的很多功能都通过依赖注...

11710

扫码关注云+社区

领取腾讯云代金券