答案:破坏死锁的循环等待和持有并等待条件可有效防止死锁。1. 按序获取锁避免循环等待;2. 使用tryLock破坏持有并等待;3. 减少锁嵌套;4. 使用定时锁超时机制,提升并发安全性。

在 Java 中防止死锁,关键在于破坏死锁产生的四个必要条件之一:互斥、持有并等待、不可抢占、循环等待。我们通常通过设计策略来避免“循环等待”和“持有并等待”,这是最有效的手段。
1. 按顺序获取锁(破坏循环等待)
如果所有线程以相同的顺序获取多个锁,就不会出现循环等待的情况。
- 为每个锁对象分配一个唯一编号
- 要求线程必须按照编号顺序获取锁
- 例如:先获取编号小的锁,再获取编号大的锁
示例:
public class SafeLock {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public void methodA() {
synchronized (lock1) {
synchronized (lock2) {
// 执行操作
}
}
}
public void methodB() {
synchronized (lock1) { // 注意:也先获取 lock1
synchronized (lock2) {
// 执行操作
}
}
}
}
2. 使用 tryLock 避免无限等待(破坏持有并等待)
使用 ReentrantLock.tryLock() 可以尝试获取锁,失败时释放已持有的资源,避免长时间占用。
立即学习“Java免费学习笔记(深入)”;
在整本书中我们所涉及许多的Flex框架源码,但为了简洁,我们不总是显示所指的代码。当你阅读这本书时,要求你打开Flex Builder,或能够访问Flex3框架的源码,跟随着我们所讨论源码是怎么工作及为什么这样做。 如果你跟着阅读源码,请注意,我们经常跳过功能或者具体的代码,以便我们可以对应当前的主题。这样能防止我们远离当前的主题,主要是讲解代码的微妙之处。这并不是说那些代码的作用不重要,而是那些代码处理特别的案例,防止潜在的错误或在生命周期的后面来处理,只是我们当前没有讨论它。有需要的朋友可以下载看看
- 尝试获取第二个锁,失败则释放第一个锁
- 可设置超时时间,避免阻塞太久
- 适合复杂场景,但代码略复杂
示例:
private final ReentrantLock lock1 = new ReentrantLock();
private final ReentrantLock lock2 = new ReentrantLock();
public boolean transferMoney(Account from, Account to, int amount) {
while (true) {
if (lock1.tryLock()) {
try {
if (lock2.tryLock()) {
try {
// 执行转账逻辑
return true;
} finally {
lock2.unlock();
}
}
} finally {
lock1.unlock();
}
}
// 可加短暂休眠避免忙等
Thread.yield();
}
}
3. 尽量减少锁的嵌套
锁嵌套越多,越容易出问题。可以通过以下方式优化:
- 合并同步代码块
- 使用更细粒度的设计或并发工具类(如 ConcurrentHashMap、AtomicInteger)
- 避免在 synchronized 方法中调用另一个对象的同步方法
4. 使用定时锁(超时机制)
使用 tryLock(long timeout, TimeUnit unit) 设置获取锁的最长等待时间。
if (lock1.tryLock(1, TimeUnit.SECONDS)) {
try {
if (lock2.tryLock(1, TimeUnit.SECONDS)) {
try {
// 正常操作
} finally {
lock2.unlock();
}
}
} finally {
lock1.unlock();
}
}
这种方式能在无法获取锁时主动退出,防止永久阻塞。
基本上就这些。只要注意锁的获取顺序、避免嵌套、合理使用 tryLock,就能在大多数场景下有效防止死锁。










