死锁及其解决
1、死锁概述
锁在开发中会经常用到,使用起来也是非常简单。但是如果业务比较复杂,使用不当的话,会出现死锁,
这是非常严重的问题。下面代码会造成死锁。
public class DeadLock {
private final static String A = "A";
private final static String B = "B";
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (A){
try{
Thread.sleep(1000l);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B){
System.out.println("A");
}
}
});
Thread t2 = new Thread(() -> {
synchronized (B){
synchronized (A){
System.out.println("B");
}
}
});
t1.start();
t2.start();
}
}
在一些复杂的场景下,可能会出现上述情况。
究其原因,t1因为某些情况没有释放掉A锁,t2在获取B锁的情况下去拿A锁,此时A锁被占用拿不到,t1在释放掉A锁的情况下,去拿B锁,此时B锁又被t2占有。导致t1,t2相互占用对方待获取的锁,且又互不相让,形成死锁。
2、排查死锁问题
windows下(测试一下)
1、打开资源管理器,查看pid;或者cmd,使用jps命令。
2、使用jstack命令,dump日志
D:\Java\jdk1.8.0_211\bin>jstack 17260 > D:/mylock.txt
3、查看日志,搜索deadlock,发现如下:
4、根据实际情况解决问题
linux下:
1、使用jps找到java进程pid
2、使用jstack命令找到该进程下线程情况
jstack pid
3、找到跟我们自己代码相关的线程
定位到waiting的线程,或者deadlock的线程
一般情下,可以先找到最消耗cpu的进程,再通过jstack定位到问题代码。
1、先找到Java进程id
ps -ef | grep java
2、找出该进程内最耗费CPU的线程,可以使用top -Hp pid
top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器。
3、将最费时的线程转化为16进制
printf “%x\n” 21742,得到21742的十六进制值为54ee
4、使用jstack打印堆栈信息
jstack 21711 | grep 54ee
死锁的产生条件
(1) 互斥条件:一个资源每次只能被一个进程(线程)使用。
(2) 请求与保持条件:一个进程(线程)因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件 : 此进程(线程)已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件 : 多个进程(线程)之间形成一种头尾相接的循环等待资源关系。
如何避免死锁
死锁的产生主要是因为相互占用对方想要获取的资源,导致相互等待。一般的解决方式如下:
1、·避免一个线程同时获取多个锁。
2、·避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
3、 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。
4、·对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。
推荐阅读