不是我吹,Lambda这个坑你肯定不知道!
上周有小伙伴反馈zk连接很慢。整理出zk连接的关键逻辑如下:
public class ClientZkAgent {
//单例模式
private static final ClientZkAgent instance = new ClientZkAgent();
private ZooKeeper zk; //zk客户端
private ClientZkAgent() {
connect(); //初始化并连接zk
}
public static ClientZkAgent getInstance() {
return instance;
}
/**
* zk常用模式: 由于zookeeper的连接是异步的,为防止zk对象在建立有效连接之前就返回,
* 我们阻塞主线程,并通过zookeeper的EventThread在连接事件中唤醒主线程
*/
private void connect() {
CountDownLatch semaphore = new CountDownLatch(1);
zk = new ZooKeeper(zkHost, timeout, watchEvent -> { // #_1
switch (e.getState()) {
case SyncConnected:
semaphore.countDown();
break;
// 其它逻辑 ....
}
});
semaphore.await(10000, TimeUnit.MILLISECONDS);
}
}
上面的代码造成第一次调用ClientZkAgent.getInstance的时候,需耗时10s, 这个时间恰好跟semaphore的超时时间相当. 在此期间,整个世界好像停滞了一样。
分析
在本地重现后,通过jstack获得系统停滞期间的线程栈,发现这个时候zookeeper的EventThread有个比较奇怪的现象:
"main-EventThread" #13 daemon prio=5 os_prio=0 tid=0x000000001fe36800 nid=0xf0c in Object.wait() [0x000000002032f000]
java.lang.Thread.State: RUNNABLE
at com.github.dapeng.registry.zookeeper.ClientZkAgent.lambda$connect$0(ClientZkAgent.java:154)
at com.github.dapeng.registry.zookeeper.ClientZkAgent$$Lambda$1/116211441.process(Unknown Source)
at org.apache.zookeeper.ClientCnxn$EventThread.processEvent(ClientCnxn.java:533)
at org.apache.zookeeper.ClientCnxn$EventThread.run(ClientCnxn.java:508)
Locked ownable synchronizers:
- None
客户端实际上很快就连上了zookeeper并返回后生成了
SyncConnected事件,而且EventThread已经在回调atcher.process方法了,但似乎事件线程就一直hold在上面#_1的位置无法往下走, 同时,lambda表达式变成了ClientZkAgent的一个方法了:lambda$connect$0。
了解了一下Java中lambda的实现方式,事情水落石出了。
简而言之,jvm会把lambda表达式转换成所在类的一个方法lambda${method}${seq}(method为该 所在的方法名,例如上面的connect方法),同时通过动态代理生成一个代理类(该代理类实现了lambda表达式所代表的具体接口),在该代理类中调用lambda${method}${seq}。
final class ClientZkAgent$$Lambda$1 implements Watcher {
final ClientZkAgent clientZkAgent;
public void process(WatchedEvent event) {
clientZkAgent.lambda$connect$0(event);
}
}
再梳理一下:
业务线程:
1.通过静态方法ClientZkAgent.getInstance()获取实例,第一次访问的时候会触发类ClientZkAgent的装载。
2.装载过程中,装载静态成员instance,这时候会尝试创建一个ClientZkAgent对象。
3.在ClientZkAgent的构造函数中连接zk,并通过CountdownLatch进入阻塞状态。注意这时候类装载还没完成。
4.CountdownLatch超时后完成对象的初始化以及整个类的加载
zk事件线程:
SyncConnected事件触发后,调用ClientZkAgent.lambda$connect$0(event), 试图唤醒业务线程(唤醒逻辑在lambda中)。
可见,在这个过程中,两个线程相互等待(类似死锁但不是死锁),直至业务线程超时后才化解这个局面。
解决
修改ClientZkAgent的初始化逻辑如下:
public class ClientZkAgent {
//单例模式
private static final ClientZkAgent instance = new ClientZkAgent();
private ZooKeeper zk; //zk客户端
private ClientZkAgent() {
}
public static ClientZkAgent getInstance() {
if (instance.zk == null) {
synchronized(ClientZkAgent.class) {
if (instance.zk == null) {
instance.connect();
}
}
}
return instance;
}
作者:Ever_00
来源:https://www.jianshu.com/p/b302123532b4
2、
3、
4、
5、
点击「阅读原文」和栈长学更多~
不是我吹,Lambda这个坑你肯定不知道!的更多相关文章
- Maven--(一个坑)在settings.xml文件中添加mirrors导致无法新建Maven项目
这是用新电脑第一次创建Maven项目--当然是一个测试项目.已经差不多忘了该怎样做,所以参考我的博客:http://www.cnblogs.com/wql025/p/4996486.html,这应该是 ...
- 关于Lambda表达式访问外部变量
在<C#高级编程>一书中提到通过Lambda表达式可以访问Lambda表达式块外部的变量 ,这是一个很好的功能(类似Js中的 闭包).但是如果没有正确的使用,会非常危险. 比如下面的事例中 ...
- 学习Spark——那些让你精疲力尽的坑
这一个月我都干了些什么-- 工作上,还是一如既往的写bug并不亦乐乎的修bug.学习上,最近看了一些非专业书籍,时常在公众号(JackieZheng)上写点小感悟,我刚稍稍瞄了下,最近五篇居然都跟技术 ...
- C# 字符串按 ASCII码 排序,注意其中的小坑
https://www.cnblogs.com/similar/p/6739293.html 在和银行做数据对接时,涉及到数据传输时的验签及加密.其中数据签名方案中就要求数据项根据属性名按 ASCII ...
- C# 参数签名字符串按 ASCII码排序,注意其中的坑
参数签名中通常是按键值对中键名称的ASCII按从小到大的顺序排序后进行hash为签名字符串.不要直接使用 SortedDictionary<string, string> 有坑的,他是按数 ...
- centos部署yapi爬坑记
前言 这几天终于完成了为期三个月的公司某个demo版的项目,在这期间和公司的后台因为API的事怼过无数次了,'我的接口没问题,是你请求的方式不对吧!'.'一定是你请求的参数不对'......诸如此类问 ...
- [转载][HASS.IO] 【HASSOS安装】成功安装HASSOS 1.9(避开了大部分坑版)
7月20日HA官方放出HASSOS说明时,我开始入坑HASSOS,经历了安装没流量.打开主页:8123没显示.HASS.IO边栏不显示.安装不了HASS.IO插件等问题之后,在8月6日总算避开了大坑进 ...
- js粘贴事件paste简单解析及遇到的坑
在用户执行粘贴操作的时候,js能够获得剪切板的内容,本文讨论一下这个问题. 目前只有Chrome支持获取剪切板中的图片数据.还好需要这个功能的产品目前只支持Chrome和Safari,一些Chrome ...
- iconfont采坑
1. iconfont采坑 1.1. 前言 使用iconfont过程中踩过坑特此记录 不知道iconfont的这里也简单介绍一笔,阿里开放的一个图标素材库,用来快速找图标下载使用图标 iconfont ...
随机推荐
- python---注册表操作
手动打开注册表 WIN+R regedit 利用QSettings 一.创建子健和键值对 settings = QSettings("HKEY_CURRENT_USER\\So ...
- 2016.09.03【初中部 NOIP提高组 】模拟赛A总结
今天班主任不知道出了什么事,把整个班留了40多分钟,各种吹水谈人生.耽误了很多时间. #$%^& 这次第一题很神奇,通过枚举矩阵来确定三个单元格的位置,没想到,结果爆零. 刚开始看见这道题有费 ...
- ef复杂模型添加
模型浏览器 函数导入-添加存储过程名称 添加复杂实体.复杂实体可以手动在xml里面创建.在complextype里面
- BZOJ 4653: [Noi2016]区间 双指针 + 线段树
只要一堆线段有重叠次数大于等于 $m$ 次的位置,那么一定有解 因为重叠 $m$ 次只需 $m$ 个线断,将那些多余的线断排除掉即可 先将区间按照长度从小到大排序,再用 $two-pointer$ 从 ...
- 珍珠x
题目描述 有n颗形状和大小都一致的珍珠,它们的重量都不相同.n为整数,所有的珍珠从1到n编号.你的任务是发现哪颗珍珠的重量刚好处于正中间,即在所有珍珠的重量中,该珍珠的重量列(n+1)/2位.下面给出 ...
- pyinstaller打包的exe太大?你需要嵌入式python玄学 探索篇
上篇我们讲到pip的安装以及普通库用pip的安装方法 CodingDog:pyinstaller打包的exe太大?你需要嵌入式python玄学 拓展篇zhuanlan.zhihu.com 问题纷沓而 ...
- 官方转译:截止2018-12-10,chromedriver与chrome对应关系表
谷歌驱动下载地址: http://npm.taobao.org/mirrors/chromedriver/ http://chromedriver.storage.googleapis.com/ind ...
- 关于tomcat的server.xml里host节点配置的一些说明
其实对这个一直搞不清楚,后来公司的网站遭遇与IIS进行整合,然后上面有几个应用需要进行配置,所以对tomcat的server.xml进行了一些深入的了解 Host的节点主要是起一个对域名解析到那个虚拟 ...
- Mac OS下Flutter环境搭建记录,VS Code开发
安装Flutter 获取FlutterSDK 终端cd进入SDK安装目录,比如 cd ~/FlutterSDK 由于在国内访问Flutter有时可能会受到限制,Flutter官方为中国开发者搭建了临时 ...
- 四、IDEA创建SpringBoot项目
1.从官网下载之后直接导入IDEA: 下载完成解压之后如下图: IDEA导入该项目: 之后一路next即可 导入成功之后你可能会发现左下角一直有个进度条在进行,傻傻的同学可能以为是在下载jar包,下个 ...