Java死锁问题解决方案及示例详解
目录
- 1、简述
- 死锁的四个必要条件:
- 2、死锁示例代码
- 3、如何检测死锁?
- 3.1 使用 jstack
- 3.2 使用 VisualVM 或 JConsole
- 4、如何预防和解决死锁?
- 4.1 统一资源获取顺序(推荐)
- 4.2 使用 tryLock() 避免无限等待
- 4.3 使用 Java.util.concurrent 包
- 4.4 死锁检测与恢复
- 5、结语
1、简述
死锁(Deadlock) 是指两个或多个线程因争夺资源而相互等待,导致所有线程都无法继续执行的一种状态。
死锁的四个必要条件:
- 互斥条件:某资源一次只能被一个线程占用。
- 占有且等待:一个线程已持有资源并等待其他线程的资源。
- 不可剥夺:资源不能被强行从线程中剥夺。
- 循环等待:多个线程形成一种头尾相接的等待资源关系链。
只要这四个条件同时满足,就可能产生死锁。
2、死锁示例代码
下面是一个典型的死锁示例:
public class DeadlockExample {
private static final Object LockA = new Object();
private static final Object LockB = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (LockA) {
System.out.println("Thread-1: locked A");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (LockB) {
System.out.println("Threadjavascript-1: locked B");
}
}
});
Thread t2 = new Thread(() -> {
synchronized (LockB) {
System.out.println("Thread-2: locked B");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (LockA) {
System.out.println("Thread-2: locked A");
}
}
});
t1.start();
t2.start();
}
}
运行这段程序可能会导致 Thread-1 等待 LockB,Thread-2 等待 LockA,从而产生死锁。
3、如何检测死锁?
3.1 使用 jstack
当应用卡住时,使用如下命令查看线程堆栈:
jps # 查找 Java 进程 ID jstack <pid>
你会看到类似:
Found one Java-level deadlock: "Thread-1": waiting to lock monitor 0x000..., which is held by "Thread-2" ...
3.2 使用 VisualVM 或 JConsole
这类工具可以图形化展示线程状态,识别死锁非常直观。
4、如何预防和解决死锁?
4.1 统一资源获取顺序(推荐)
确保多个线程获取多个锁时 按相同顺序加锁。
public void safeMethod() {
synchronized (LockA) {
synchronized (LockB) {
// 安全操作
}
}
}
4.2 使用 tryLock() 避免无限等待
使用 ReentrantLock 的 tryLock() 方法设置获取锁的超时时间。
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;
public class TryLockExample {
private final ReentrantLock lockA = new ReentrantLock();
private final ReentrantLock lockB = new ReentrantLock();
public void run() {
Thread t1 = new Thread(() -> {
try {
if (lockA.tryLock(1, TimeUnit.SECONDS)) {
javascript System.out.println("Thread-1: locked A");
Thread.sleep(100);
if (lockB.tryLock(1, TimeUnit.SECONDS)) {
System.out.println("Thread-1: locked B");
lockB.ujsnlock();
}
lockA.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t2 = new Thread(() -> {
try {
if (lockB.tryLock(1, TimeUnit.SECONDS)) {
System.out.println("Thread-2: locked B");
Thread.sleep(100);
if (lockA.tryLock(1ZNgkPO, TimeUnit.SECONDS)) {
System.out.println("Thread-2: locked A");
lockA.unlock();
}
lockB.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
t2.start();
}
public static void main(String[] args) {
new TryLockExample().run();
}
}
4.3 使用 java.util.concurrent 包
避免使用低级的 synchronized,使用高php级并发工具如 ExecutorService、Semaphore、Lock 等更可控的工具。
4.4 死锁检测与恢复
一些大型系统中,可以通过定期扫描线程状态,自动检测死锁并重启部分线程或服务。例如通过自定义 ThreadMXBean 检测死锁。
最佳实践小结:
| 技术手段 | 说明 |
|---|---|
| 加锁顺序统一 | 所有线程按相同顺序获取资源 |
| tryLock + timeout | 尝试加锁失败后避免长时间阻塞 |
| lock 分解 | 将大锁拆分成小锁减少竞争 |
| 并发工具替代 synchronized | 使用 ReentrantLock、Semaphore 等 |
| 死锁检测工具 | 使用 jstack、VisualVM 等工具 |
5、结语
死锁是多线程编程中不可忽视的问题,但并不是无法避免。只要我们在设计时保持锁的有序性,并结合现代并发工具进行控制,绝大多数死锁问题都是可以预防的。
以上就是Java死锁问题解决方案及示例详解的详细内容,更多关于Java死锁问题解决的资料请关注编程客栈(www.devze.com)其它相关文章!
加载中,请稍侯......
精彩评论