代码评审阶段即可拦截80%死锁隐患,关键盯住四大高危模式:锁顺序不一致、synchronized块内阻塞调用、锁对象共享边界不清、tryLock缺乏完整回退路径。

代码评审阶段就能揪出死锁隐患,关键不是等运行时出问题,而是盯住几个高危模式——这些模式在真实项目里反复出现,只要评审时多问一句“锁顺序是否一致”“有没有在锁里调远程”,基本能拦下 80% 的死锁。
重点检查多把锁的获取顺序
死锁最常见根源就是锁顺序不统一。比如转账方法 transfer(Account from, Account to),如果内部直接按参数顺序加锁:synchronized(from) { synchronized(to) { ... } },那当线程 A 调用 transfer(acc1, acc2)、线程 B 调用 transfer(acc2, acc1) 时,就天然形成反向加锁链。
评审时要确认:
- 所有涉及多个锁的操作,是否强制按统一规则排序(如账户 ID 数值升序、字符串字典序)
- 有没有把业务参数(如 ID、名称)直接当作锁对象,又没做标准化排序
- 工具类或通用方法(如 DAO 层批量更新)是否隐含多锁逻辑,且未声明锁顺序契约
警惕 synchronized 块内的阻塞操作
锁本身不危险,危险的是拿着锁还去干慢事。评审时看到 synchronized 块里有以下代码,就要打问号:
立即学习“Java免费学习笔记(深入)”;
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 数据库查询或事务提交(特别是未设超时的 JDBC 调用)
- HTTP/RPC 远程调用(没配 connect/read timeout)
- 文件读写、Socket 通信、第三方 SDK 同步方法
- 另一个
synchronized块或ReentrantLock.lock()
这类写法会让持锁时间不可控,大幅提高死锁概率,也容易引发线程池耗尽。
核对锁粒度与共享对象一致性
有些锁看似独立,实则指向同一底层对象。典型陷阱包括:
-
Collections.synchronizedMap(map)返回的包装器,其内部锁就是map实例本身;若外部又写synchronized(map) { ... },等于重复加同一把锁,还可能卷入其他同步块 - 多个 Service 方法都对同一个 Spring Bean(如单例工具类)加锁,而该 Bean 又被其他模块复用
- 静态锁(
private static final Object LOCK = new Object())被跨业务场景共用,但各处加锁逻辑彼此 unaware
评审时建议用 IDE 全局搜索锁对象名或哈希码,确认它的使用范围是否可控、边界是否清晰。
关注 tryLock 的超时与回退逻辑
如果用了 ReentrantLock.tryLock(long, TimeUnit),不能只看“用了 tryLock”就放心。要检查:
- 超时时间是否合理(比如 100ms 对数据库操作太短,对内存计算又太长)
- 获取失败后是否真正释放了已持有的其他资源(避免半开状态)
- 是否有重试机制,以及重试是否引入新锁竞争(比如重试时换了锁顺序)
没有完整回退路径的 tryLock,只是把死锁变成“偶发失败”,问题依然在。

















