一、定义
多个进程因抢夺系统资源而产生相互等待的现象。
二、场景模拟
package com.gui.practise.thread.deadlock;
public class DeadLock {
private final Object resource1 = new Object();//资源 1
private final Object resource2 = new Object();//资源 2
public void leftRight() {
synchronized (resource1) {
System.out.println(Thread.currentThread() + "get resource1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "waiting get resource2");
synchronized (resource2) {
System.out.println(Thread.currentThread() + "get resource2");
}
}
}
public void rightLeft() {
synchronized (resource2) {
System.out.println(Thread.currentThread() + "get resource2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "waiting get resource1");
synchronized (resource1) {
System.out.println(Thread.currentThread() + "get resource1");
}
}
}
}
package com.gui.practise.thread.deadlock;
public class DeadLockTest {
public static void main(String[] args) {
DeadLock dl = new DeadLock();
new Thread(() -> {
dl.leftRight();
}, "线程 1").start();
new Thread(() -> {
dl.rightLeft();
}, "线程 2").start();
}
}
三、死锁检测
Java自带工具检测死锁
第一步:jps找到对应进程
第二步:jstack查看对应堆栈信息
阿里巴巴开源的java诊断工具arthas
下载地址:https://github.com/alibaba/arthas/releases
四、特点&预防&解除
特点
- 互斥(多个线程不能同时使用一个资源)
- 不可剥夺(资源请求者不能强制从资源占有者手中抢夺资源,资源只能由占有者主动释放)
- 持有并等待(当资源请求者在请求其他资源的同时保持对原有资源的占有)
- 环路(多个线程存在环路的锁依赖关系而永远等待下去)
预防
- 能无锁开发就无锁开发
- 如果确实需要使用锁,核心原则:资源有序分配法(各个线程总是以相同的顺序申请自己想要的资源)
- 合理的锁顺序
- 更小的锁范围
- 使用定时锁,比如Lock类中的tryLock方法
解除
- 直接重启服务,如果代码未进行修复,问题还会出现
- 使用定时锁方案,Lock的tryLock方法
五、使用资源有序分配法解除场景模拟中的死锁问题
package com.gui.practise.thread.deadlock;
public class DeadLock {
private final Object resource1 = new Object();//资源 1
private final Object resource2 = new Object();//资源 2
public void leftRight() {
synchronized (resource1) {
System.out.println(Thread.currentThread() + "get resource1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "waiting get resource2");
synchronized (resource2) {
System.out.println(Thread.currentThread() + "get resource2");
}
}
}
public void rightLeft() {
synchronized (resource1) {
System.out.println(Thread.currentThread() + "get resource1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "waiting get resource2");
synchronized (resource2) {
System.out.println(Thread.currentThread() + "get resource2");
}
}
}
}