写于:2020-03-31

一、什么是死锁

百度百科

wiki

二、程序出现死锁的几种情况

1、交叉锁导致的线程死锁

《线程安全与数据同步-sychronized关键字需要注意的几个问题》
中提到了死锁的问题。

该问题,如下图:
01.png
线程 T1 持有 R1 的锁,等待获取 R2 的锁。而 线程 T2 持有 R2 的锁,等待获取 R1的锁,于是出现的死锁。

2、内存不足导致的死锁

当并发请求系统可用内存时,如果此时系统内存不足,可能导致出现死锁。

例如:两个线程 T1 和 T2,T1 获取到了 10 M 的内存用于运行,T2 获取到了 20M 的内存用于运行。如果此时每个线程的执行单元都还需要 30M 的内存,而剩余的内存刚好20 M,那么两个线程有可能都在等待彼此释放内存资源,而导致死锁。

3、一问一答式的数据交换

服务端开启某个端口,等待客户端访问,客户端发送请求立即等待接收,由于某种原因服务端错过了客户端的请求,双方陷入互相等待的状态从而进入死锁状态。

4、数据库锁

无论是数据库表级别的锁,还是行级别的锁,比如某个线程执行 for update 语句退出了事务,其他线程访问该数据库时都将陷入死锁。

5、文件锁

某县城获得了文件锁意外退出,其他读取改文件的线程将陷入死锁直到系统释放文件句柄资源。

6、死循环引起的死锁

程序由于代码原因或者对某些异常处理不当,进入死循环,导致的假死状态。

三、通过几个案进行死锁诊断

1、交叉锁导致的线程死锁

《线程安全与数据同步-sychronized关键字需要注意的几个问题》 中的相关案例提到了使用 jconsole 查看死锁信息。

通过 jstack 同样能够查看到信息,运行上述案例代码,通过 jstack 查看信息

jps
jstack pid

信息如下
02.png

2、死循环导致的线程死锁

通过 spring boot web 项目通过 API 的方式在虚拟机测试,死循环会导致 CPU 高位负载,

step1、测试代码如下

@RequestMapping("cpu")
public void cpuTest(){
    while (true){
    }
}

step2、启动 java 程序,并查看此时的 java 程序的负载情况

03.png

step3、访问 API,造成死循环,此时查看 java 程序负载

04.png
发现此时的负载已经打满。

step4、进行问题定位

出现死循环的问题代码实在 java 线程中执行,找到出 java 应用程序外的负载打满的其他java线程即可定位问题。

a、使用top命令定位 高位cpu线程(shift + H 切换线程模式)
05.png
b、用jstac 导出 java 应用程序进程的 dump

jstack 8258 > dump.txt

c、将跑满的 java应用的线程的 pid 转成16进制

printf "%x \n" 8281

结果:2059

d、查看 dump.txt 并搜索 2059
06.png
定位到问题代码,通过查询,查到死循环代码