什么是线程死锁
死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的互相等待的现象,
在无外力作用的情况下,这些线程会 直相互等待而无法继续运行下去。如图所示
在图中,线程A已经持有了资源2, 同时还想申请资源1,线程B已经持有了资源 1,它同时还想申请资源2 ,
所以线程A和线程B就因为相互等待对方已经持有的资源,而进入了死锁状态。
死锁的必要条件
- 互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待
- 不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。
- 请求并保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放
- 循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求。即存在一个处于等待状态的进程集合{Pl, P2, …, pn},其中Pi等 待的资源被P(i+1)占有(i=0, 1, …, n-1),Pn等待的资源被P0占有
程序出现死锁怎么排查
让我们看一段代码,这段代码会出现死锁
public class DeadLockDemo {
private static String A = "A";
private static String B = "B";
public static void main(String[] args) {
new DeadLockDemo().deadLock();
}
private void deadLock() {
Thread t1 = new Thread(()->{
synchronized (A) {
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (B) {
System.out.println("1");
}
}
});
Thread t2 = new Thread(()->{
synchronized (B) {
synchronized (A) {
System.out.println("1");
}
}
});
t1.start();
t2.start();
}
}
一旦出现死锁,业务是可感知的,因为不能继续提供服务了,那么只能通过dump线程看看是哪个线程出现了问题
-
jps
查看java
进程,找到死锁进程的进程号 -
使用
jstack 5632 > ~/Downloads/dump5632
导出dump文件 -
查看dump文件内容,这里我们看到如下内容
"Thread-1" #12 prio=5 os_prio=31 tid=0x00007fe95a86a000 nid=0x5703 waiting for monitor entry [0x00007000043ea000] java.lang.Thread.State: BLOCKED (on object monitor) at java并发编程的艺术.ch01.DeadLockDemo.lambda$deadLock$1(DeadLockDemo.java:34) - waiting to lock <0x000000076ac28aa0> (a java.lang.String) - locked <0x000000076ac28ad0> (a java.lang.String) at java并发编程的艺术.ch01.DeadLockDemo$$Lambda$2/1329552164.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) "Thread-0" #11 prio=5 os_prio=31 tid=0x00007fe95a869000 nid=0xa803 waiting for monitor entry [0x00007000042e7000] java.lang.Thread.State: BLOCKED (on object monitor) at java并发编程的艺术.ch01.DeadLockDemo.lambda$deadLock$0(DeadLockDemo.java:27) - waiting to lock <0x000000076ac28ad0> (a java.lang.String) - locked <0x000000076ac28aa0> (a java.lang.String) at java并发编程的艺术.ch01.DeadLockDemo$$Lambda$1/381259350.run(Unknown Source) at java.lang.Thread.run(Thread.java:748)
我们知道是
DeadLockDemo
的第32行和27行引起的死锁
避免死锁的常见方法
最后介绍下避免死锁的常见方法
- 避免一个线程同时获取多个锁
- 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源
- 尝试使用定时锁,使用
lock.tryLock(time)
来替代使用内部锁机制 - 对于数据库锁,加锁和解锁必须在一个库连接里,否则会出现解锁失败的情况