死锁现象描述
现在有两把锁(排他锁),锁1和锁2,两个线程,线程A和线程B,线程A获取到锁1,线程B获取到锁2,此时线程A再去获取锁2,线程B再去获取锁1,就会造成死锁。
死锁在生产上表露出来的现象通常是cpu占用不高且响应缓慢
死锁问题排查
测试程序如下:
public class DeadLockTest {
public static Object lock1 = new Object();
public static Object lock2 = new Object();
public static void main(String[] args) {
new Thread(()->{
synchronized (lock1) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (lock2) {
System.out.println(111111);
}
}
}).start();
new Thread(()->{
synchronized (lock2) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (lock1) {
System.out.println(111111);
}
}
}).start();
}
}
在测试程序中很容易发现死锁问题,但是在生产上该如何定位呢?
第一步:定位问题
通过jps指令找到进程号
通过jstack <pid> 指令可以查看该进程的线程状态
注意:当生产发生问题时,通常会将此信息输出到文件中,可使用如下指令
jstack -l <pid> > <文件名>
在控制台最下面有死锁提示:
第二步:解决问题
产生死锁有四个必要条件,分别是资源互斥、环路等待、不可剥夺、请求并保持。破坏任一条件即可解决死锁。
- 资源互斥:如果资源可以共享,那么就不会互相阻塞,也就不会产生死锁了
- 环路等待:如果获取锁1和锁2的时候按照顺序获取,那么也不会产生死锁
- 不可剥夺:线程A获取锁2的时候直接剥夺线程B获取到的锁2
- 请求并保持:线程A发现锁2获取不到(设定一定的超时时间),则释放锁1,此时线程B可以获取到锁,运行完毕之后,线程A再重新执行。