目录
0 引 言
在java中我们常常使用加锁机制来确保线程安全,但是如果过度使用加锁,则可能导致锁死锁。那么在生产中如何快速定位是否发生了死锁的故障呢?本文将从二个步骤进行分析,首先定位进程号,其次通过找到的进程号再进一步确认是否发生了死锁现象,最终完美定位出故障来。本文对于死锁的分析给出了具体的死锁测试代码,及造成死锁的原因分析,并进一步地给出了排查死锁故障的方法,本方法已在具体的实际中得到验证。
通过本文你将获得以下知识:
- (1)什么是死锁
- (2)死锁的具体代码编写
- (3)死锁的原因分析
- (4)死锁的故障排查
1 什么是死锁
死锁是指两个或两个以上的进程(线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无力推进下去。如果系统资源充足,进程的资源请求都能得到满足,死锁出现的可能性就很低,否则就会因为争夺有限的资源陷入死锁。
注:进程和线程都可以发生死锁,只要满足死锁的条件!
2 死锁代码测试用例
(1)测试类编写
import java.util.concurrent.TimeUnit;
class HoldThread implements Runnable {
private String lockA;
private String lockB;
public HoldThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName() + "\t 自己持有锁" + lockA + "尝试获得" + lockB);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName() + "\t 自己持有锁" + lockB + "尝试获得" + lockA);
}
}
}
}
public class DeadLockDemo{
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new HoldThread(lockA,lockB),"threadA").start();
new Thread(new HoldThread(lockB,lockA),"threadB").start();
}
}
(2)执行结果查看
(3) 该代码造成死锁原因分析
t1线程首先获得A锁,再获得B锁,t2线程先获得B锁,再获得A锁,当t1获得A锁的同时,希望获得B锁,但此时t2
先获得了B锁的使用权,此时t1无法获取B锁,同理t2线程也无法获得A锁,线程一直等待。
这种现象也叫锁抱死。
3 定位分析方法
3.1 到windows 命令窗口使用jps -l命令定位进程号
可以看到造成故障的进程号为2992
3.2 jstack -l +进程号找到死锁查看
jstack -l 2992
4 小 结
本文综合分析了死锁产生的原因及其具体故障排查策略,通过jps命令及jstack命令可以快速定位死锁故障,该方法已经在实际中得到良好的运用,并起到了积极的效果。