Java死锁的例子
创建两个线程,每个线程先锁定一个对象,再尝试获取对方持有的对象。当线程1锁定A后请求B,线程2锁定B后请求A时,形成循环等待导致死锁。核心代码使用synchronized嵌套锁,通过Thread.sleep(100)确保锁竞争顺序,运行后线程会相互阻塞无法继续执行。
public class DeadlockDemo {// 定义两个不可变的锁对象private static final Object lock1 = new Object();private static final Object lock2 = new Object();
public static void main(String[] args) {// 线程1:先获取lock1,再尝试获取lock2Thread thread1 = new Thread(() -> {synchronized (lock1) {System.out.println("Thread 1: 持有 lock1");try {// 休眠确保线程2能获取lock2Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread 1: 等待 lock2...");synchronized (lock2) {System.out.println("Thread 1: 同时持有 lock1 和 lock2");}}});
// 线程2:先获取lock2,再尝试获取lock1Thread thread2 = new Thread(() -> {synchronized (lock2) {System.out.println("Thread 2: 持有 lock2");System.out.println("Thread 2: 等待 lock1...");synchronized (lock1) {System.out.println("Thread 2: 同时持有 lock1 和 lock2");}}});
thread1.start();thread2.start();}
}
死锁产生过程分析:
-
线程启动顺序
-
thread1
先获取lock1
并休眠 100ms(此时未释放lock1
) -
thread2趁休眠期获取lock2
-
-
阻塞等待
-
thread1
醒来后尝试获取lock2
(但thread2
正持有lock2
) -
thread2同时尝试获取lock1(但thread1正持有lock1)
-
-
循环等待 两线程互相持有对方需要的锁,且都不释放,形成永久阻塞。
验证死锁方法(命令行操作):
jps # 查看Java进程ID(假设进程ID为12345)
jstack 12345 # 分析线程堆栈
输出结果会显示:
Found one Java-level deadlock:
"Thread-1":waiting to lock monitor 0x00007fdf0e80ed68 (held by Thread-0)
"Thread-0":waiting to lock monitor 0x00007fdf0e8100a8 (held by Thread-1)
关键注意点:
-
必要条件 死锁需同时满足:互斥使用、不可抢占、请求与保持、循环等待。
-
确定性设计 示例中
Thread.sleep(100)
是为确保死锁必然发生(模拟实际业务中的操作延迟)。若无此延迟,可能因线程执行速度过快而规避死锁。
解决思路(代码改进方向):
-
统一锁顺序 所有线程按固定顺序获取锁(如都先获取
lock1
再lock2
)。 -
超时机制 使用
ReentrantLock.tryLock(1, TimeUnit.SECONDS)
替代synchronized
,超时后释放已有锁并重试。
可通过 JConsole 或 VisualVM 实时监控线程状态和死锁检测。实际开发中建议使用静态代码分析工具提前发现潜在死锁风险。
死锁产生条件:
-
互斥:synchronized保证锁的互斥性。
-
持有并等待:线程A持有lock1并等待lock2,线程B持有lock2并等待lock1。
-
不可抢占:锁只能由持有线程释放。
-
循环等待:线程A等待线程B,线程B等待线程A。