写于:2020-03-31
一、什么是死锁
二、程序出现死锁的几种情况
1、交叉锁导致的线程死锁
在 《线程安全与数据同步-sychronized关键字需要注意的几个问题》
中提到了死锁的问题。
该问题,如下图:
线程 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
2、死循环导致的线程死锁
通过 spring boot web 项目通过 API 的方式在虚拟机测试,死循环会导致 CPU 高位负载,
step1、测试代码如下
@RequestMapping("cpu")
public void cpuTest(){
while (true){
}
}
step2、启动 java 程序,并查看此时的 java 程序的负载情况
step3、访问 API,造成死循环,此时查看 java 程序负载
发现此时的负载已经打满。
step4、进行问题定位
出现死循环的问题代码实在 java 线程中执行,找到出 java 应用程序外的负载打满的其他java线程即可定位问题。
a、使用top命令定位 高位cpu线程(shift + H 切换线程模式)
b、用jstac 导出 java 应用程序进程的 dump
jstack 8258 > dump.txt
c、将跑满的 java应用的线程的 pid 转成16进制
printf "%x \n" 8281
结果:2059
d、查看 dump.txt 并搜索 2059
定位到问题代码,通过查询,查到死循环代码