背景
昨天早上来公司就被喊着看一个bug,这个bug的内容就是点击一个按钮,服务一直没有响应,前端的页面效果在那一直转圈圈。
排查问题
- 去看是什么场景发生的这个bug.
- 去看相关接口.
- 根据相关接口去看kibana日志.
在排查日志的时候发现有一个日志很奇怪……一直报:为了获取锁,已经等待了xx秒
按这个秒数换算下来已经是等了大半天了。第一反应就是死锁了,不然不至于等那么久。
- 根据这行日志的文字去代码里定位具体代码。(简要代码如下)
private final AtomicBoolean inProcessing = new AtomicBoolean(false);
protected void wait4WorkLock() {
boolean canWork;
do {
canWork = getLock();
if (!canWork) {
sleepForMillisecond(500);
}
} while (!canWork);
}
private boolean getLock() {
return inProcessing.compareAndSet(false, true);
}
这三块的代码挺容易理解的,定义一个原子布尔类型当作锁,初始是false,如果成功将false变成true就表示获得到了锁。
- 由于用的是cas做锁的,那么猜测死锁的可能性是:嵌套地去获取cas锁了
伪代码:
public void function() {
// 获得锁,做A事情
wait4WorkLock();
if(a == b) {
functionB();
}
unLock();
}
public void functionB() {
// 获得锁,做B事情
// 在这个例子里这儿会造成死锁,死等待,因为在function方法里布尔原子已经是true了,不能在true的情况下改成true
wait4WorkLock();
functionB();
unLock();
}
- 带着这个去代码里求证,果然……
问题的根本原因
原子布尔类型不支持重入,不是可重入锁,对于同一个线程不能多次获取。
解决办法
确认了问题的根本原因,那么解决办法就有很多了。
- 使用synchronized关键字。
- 使用reentrantlock。
总结
- 线上的死锁问题分析
- 将cas锁换成可重入锁。