偶遇 static 初始化死锁
source link: https://blog.yuantops.com/tech/deadlock_in_static_initialization/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
偶遇 static 初始化死锁
最近开发新系统,用到了内存数据库 H2 web . 上线时,遇到问题:服务启动流程卡住,不报错,也起不来。
用 `jstack` 查看栈信息,main thread 状态为 RUNNABLE,另外一个线程 `H2 Console Server` 状态也为 RUNNABLE。仔细观察, main
栈包含一段可疑信息: -locked <xxxx> (a java.lang.Class for org.h2.Driver)
,疑似线程被锁。
有两个诡异之处:1. 服务启动阻塞具有偶然性,有的机器(特别是性能比较差的机器)大概率不会卡。2. 虽然阻塞了,但此时 h2 数据库 web console 已经成功起来了。WHY??
作为应急措施,在启动 H2 web 之后,立马让当前线程停一会儿(Thread.sleep(500)
),服务顺利启动。接下来,是我慢慢和它死磕的过程。
单步调试法 + 日志大法。
调试小技巧:如果类在依赖的 jar 里,又想在这个类里打日志,只需按它的包结构在 IDE 里对应建立目录层级,再把源码拷贝进去就好了。
最后发现了问题:在类静态方法加载过程中,形成了死锁。
下面是摘出来的问题代码:
import org.h2.Driver;
import org.h2.tools.Server;
import java.sql.SQLException;
public class Main {
public static void main(String[] args1) throws InterruptedException {
int port = 9081;
Server webServer = null;
String[] args = ("-web,-webAllowOthers,-trace,-webPort," + port).split(",");
try {
webServer = org.h2.tools.Server.createWebServer(args);
webServer.start();
} catch (SQLException e) {
String msg = "h2 web server create failed";
throw new IllegalStateException(msg, e);
}
System.out.println("["+Thread.currentThread().toString()+"]尝试加载 driver " + System.currentTimeMillis());
Thread.sleep(32);
Driver driver = new Driver();
System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!看到我,说明死锁重现失败。多试几次!!!!!!!!!!!!");
}
}
- main() 方法运行的线程,为 [main] 线程。
执行到第 14 行,执行 webServer.start() 方法后,第二个线程 [H2 Console Thread] 将被启动(通过单步追踪,可以定位具体代码)。具体流程为: 2.1. 创建 ServerSocket, 开始监听服务端端口 2.2. 新建名为[H2 Console Thread] 的线程,每当服务端口读到了数据,则 2.3. 新分配一个线程,处理数据流。
在 2.1 ,为了确认 ServerSocket 成功建立,会尝试与服务端口号建立一个 socket,一旦成功,则认为服务端口已经起来, 并关闭 socket。
2.3 中创建的新线程,读到一个空白文件流,抛出 EOF 异常。在异常处理类
DbException.java
中,会加载 DriverManager.java (详见代码)。进入 static {} 后,持有了锁B。同时在 static 方法中,它通过 ServiceLoader 尝试加载所有 Driver,这意味着,它要等待 Driver.java 初始化的锁(锁A)main 线程中,在故意等待了一小段时间(第21行)后,main() 方法继续执行。在第 22 行创建 driver 实例的过程中,进入static {} 方法,持有了 Driver.java 的锁(锁A)。同时,在 static {} 中,尝试 load DriverManager.java,需要等待 DriverManager.java 的锁(锁B)
两个线程,彼此等待对方持有的锁。也就形成了死锁。
自问自答: 初始化存在锁??
参考回答: https://stackoverflow.com/questions/878577/are-java-static-initializers-thread-safe
静态代码块 static {} 是线程安全的,同时只能在一个线程中运行。
类的初始化,确实存在锁。
自问自答: 为什么 Thread.sleep(500)
有用?
死锁的本质在资源争抢。加上 Java 类加载存在缓存机制,只要让一个线程先执行完,就破解了锁。
另外,搜到一篇类似博文,都是由 DriverManager
在多个线程被初始化形成锁: Avoiding deadlock when using Multiple JDBC Drivers in an Application。
Recommend
-
74
豪车、私人飞机等这才是富豪们大佬们的标配,日前有网友偶遇王健林坐地铁,这让众人都感到想十分稀奇。据悉,昨天,有网友称在北京地铁1号线地铁偶遇万达董事长王健林并晒出了王健林乘坐地铁的照片。对此,有网友猜测,想必是因为严重堵车,又有重要事情处理只能被...
-
48
来源:汽车商业周刊原标题:偶遇贾跃亭文/贾可参观所有的地方,包括一般不让人去的造型中心。美国西部时间2018年4月2日上午9时,《汽车商业评论》记者被允许在洛杉矶FaradayFuture汽车公司研发中心参观。这里似乎是和硅谷的文化氛围一样
-
69
记仇50年:男子偶遇儿时打过自己的邻居,将其打骨折
-
58
IT之家5月19日消息昨日,马云在香港大学第199届学位颁授典礼上获得名誉社会科学博士学位,这是马云获得的第五个博士学位。当天,网友在香港铜锣湾的一个便利店偶遇马云,从图片来看,马云正在使用支付宝付款码购买报纸。▲来源自网友微博不少网友纷纷
-
33
新浪娱乐讯据网友“YLW宁波网友”爆料,5月28日下午,刘强东奶茶妹妹夫妻俩现身三里屯喝下午茶,奶茶妹妹棒球帽墨镜遮脸认真选饮料,两人低调坐在靠墙边位置,不时聊天显甜蜜。
-
57
【偶遇汽车自燃,洒水车唱着歌把火灭了】6月29日,河南郑州。一辆停在路边的轿车发生自燃,一时找不到车主。此时,一辆龙湖环卫的洒水车路过,洒水车随即变身“消防车”,唱着歌就把火灭了。
-
29
憨豆先生Rowan Atkinson讲偶遇路人的趣事,被路人说“你长得简直和憨豆先生一模一样”,但对方却死活不肯相信他就是本尊……
-
33
1980年,邓小平偶遇《白发魔女》剧组,
-
48
大V机场偶遇余承东:Mate X体验超爽 上市还要等等 2019年07月25日 09:28 16012 次阅读 稿源:
-
58
作者关联了问题:“程序挂起,没响应了”大家帮忙看下什么原因从线程 Dump 文件中看,应用线程都处于 runnable 状态,但实际上,程序却被挂起,不能继续往下执...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK