jdk lambda表达式的坑
整理出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 -> { // #_1switch (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: RUNNABLEat 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表达式转换成所在类的一个方法lambdamethod{method}method{seq}(method为该 lambda 所在的方法名,例如上面的connect方法),同时通过动态代理生成一个代理类(该代理类实现了lambda表达式所代表的具体接口),在该代理类中调用lambdamethod{method}method{seq}。
在上面的例子中,生成的代理类大概如下:
final class ClientZkAgent$$Lambda$1 implements Watcher {final ClientZkAgent clientZkAgent;public void process(WatchedEvent event) {clientZkAgent.lambda$connect$0(event);}
}
业务线程:
- 通过静态方法ClientZkAgent.getInstance()获取实例,第一次访问的时候会触发类ClientZkAgent的装载。
- 装载过程中,装载静态成员instance,这时候会尝试创建一个ClientZkAgent对象。
- 在ClientZkAgent的构造函数中连接zk,并通过CountdownLatch进入阻塞状态。注意这时候类装载还没完成。
- CountdownLatch超时后完成对象的初始化以及整个类的加载。
zk事件线程:
- SyncConnected事件触发后,调用ClientZkAgent.lambda$connect$0(event), 试图唤醒业务线程(唤醒逻辑在lambda中)。
- 然而这时候ClientZkAgent还没加载完,事件线程只能等待类加载流程的结束。
- 业务线程加载完ClientZkAgent后,事件线程完成事件的处理。
可见,在这个过程中,两个线程相互等待(类似死锁但不是死锁),直至业务线程超时后才化解这个局面。
解决方法:
修改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;}
jdk lambda表达式的坑相关推荐
- 关于JDK lambda表达式与匿名内部类的等价实现却出现了截然不同的结果原因分析
发现了一个很奇怪的现象,先上代码: public interface A {int f();default A cf(A other){return new A(){@Overridepublic i ...
- 【Java】jdk 1.8 新特性——Lambda表达式
Lambda表达式 jdk 1.8 新加入的特性,简化了简单接口的实现 函数式接口 函数式中只有一个待实现的方法,可以使用@FunctionalInterface注解标注函数式接口.这个接口中只能有一 ...
- java jdk 1.8中lambda表达式常用方法
在平常的开发工作当中,经常需要对数组进行一些操作,比如根据某个属性值分组,取出某个属性值作为数组等.那么,jdk 1.8为我们提供了便捷的方法,我们应该怎么使用呢? 1:filter:根据某个属性值过 ...
- JDK 8的新特性-Lambda表达式 精品文章总结
文章目录 一. 前言 1.2 认识Lambda表达式 二. Lambda 表达式的格式 2.1 语法格式一: 无参数,无返回值,Lambda体只有一条语句 2.2 语法格式二: 有一个参数,并且无返回 ...
- jdk 8 中 Lambda 表达式练习题(经典面试题)
jdk 8 中 Lambda 表达式练习题(经典面试题) 题1 调用 Collection.sort()方法,通过定制排序比较两个Employee(先按年龄,年龄相同按姓名比). 题2 ①声明函数式接 ...
- Java笔记整理五(Iterator接口,泛型,常见数据结构(栈,队列,数组,链表,红黑树,集合),jdk新特性,异常,多线程,Lambda表达式)
Java笔记整理五 1.1Iterator接口 Collection接口与Map接口主要用于存储元素,而Iterator主要用于迭代访问(即遍历)Collection中的元素,因此Iterator对象 ...
- JDK 8 新特性 之 Lambda表达式
前言 Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性. Lambda 允许把函数作为参数传递进方法中. 使用 Lambda 表达式可以使代码变的更加简洁紧凑. lamb ...
- Lambda表达式入门,看这篇就够了!
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:海向 cnblogs.com/haixiang/p/1102 ...
- java函数式编程_Java 函数式编程和 lambda 表达式详解
作者:DemonsI my.oschina.net/demons99/blog/2223079 为什么要使用函数式编程 函数式编程更多时候是一种编程的思维方式,是种方法论.函数式与命令式编程的区别主要 ...
- 10个Java 8 Lambda表达式经典示例
Java 8 刚于几周前发布,日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表 达式,它将允许我们将行为传到函数里.在J ...
最新文章
- 新手必看,17 个常见的 Python 运行时错误
- ​谷歌大神Jeff Dean领衔,万字展望5大AI趋势
- 立刻停止使用AUFS,开启Overlay!
- CVS/SVN 托管服务
- C++ Primer 5th笔记(7)chapter7 类
- /dev 设备文件属性解读
- oracle11g ora 29927,Oracle11gR2使用RMANDuplicate复制数据库
- 经典面试题(28):以下代码将输出的结果是什么?
- php验证码显示碎图片,我的验证码只显示破碎的小图片
- 五大维度深掘工业互联网数据价值
- Ctrix-XenApp中误删应用服务器,如何重新添加
- 随机梯度下降算法(SGD)
- 数据库原理与应用实验3--(数据库的简单查询和连接查询)
- 如何在线下载哔哩哔哩上的视频
- codeblocks 编译器设置方法 也可以酱紫滴
- 雪花飘落代码java_JavaScript实现雪花飘落效果
- python+opencv实现相似图片的搜索
- 用angular JS和 bootstrap完成一个简单的购物车界面
- SNS网店软文推广法
- word研究报告排版要领
热门文章
- 大数据学情分析_大数据时代|如何轻松做好学情分析
- 谷粒商城:18.性能压测
- mysql函数commit_phpmysqli_commit()函数和mysqli_autocommit()函数比较
- 华为算法精英赛(题3:概率计算)
- 随手记_科研攻略_好的idea的产生
- 目标检测(8):CenterNet-Objects as Points-将目标建模为边界框中心点的方法
- 人工智能原理知识点对照
- 人工智能领域有哪些曾被拒稿的优秀工作?
- window.onload和$(document).ready()比较
- 《实时控制软件设计》团队项目第三天工作日志