死锁预防需从设计阶段切断其四个必要条件:互斥、占有并等待、不可剥夺、循环等待;常用策略包括按序加锁、tryLock超时回退、减小锁粒度、避免嵌套隐式加锁。
死锁在Java中通常发生在多个线程互相持有对方需要的锁,且都不释放,导致所有相关线程永久阻塞。要避免死锁,关键不是等它发生再排查,而是从设计阶段就切断死锁形成的必要条件。
理解这四点,才能对症下药:
最简单有效的预防方式——为所有锁定义全局唯一顺序,所

例如操作两个账户转账,约定始终先锁id小的账户,再锁id大的:
if (from.getId() < to.getId()) {
synchronized(from) {
synchronized(to) { /* 转账逻辑 */ }
}
} else {
synchronized(to) {
synchronized(from) { /* 转账逻辑 */ }
}
}这样无论哪个线程发起转账,加锁顺序总是一致,循环等待就不复存在。
用ReentrantLock的tryLock(long, TimeUnit)代替无条件阻塞的lock(),给获取锁设置超时。失败后主动释放已持锁,并重试或放弃。
锁得越少、越短,冲突概率越低,死锁机会自然下降:
容易被忽视的陷阱:方法A加了锁,内部调用的方法B也尝试获取另一把锁,而B可能被其他线程以不同顺序调用。
建议: