避免死锁需统一加锁顺序并用jstack定位,核心是打破循环等待;优先按hashcode或id排序锁,封装工具方法确保顺序,避免同步块内调用外部方法,高风险场景改用reentrantlock配合超时与finally释放,减少嵌套锁、拆分粒度、慎用synchronized(this)。

避免 synchronized 死锁,核心是打破“循环等待”这个可干预的条件;排查则靠 jstack 快速定位锁持有与等待关系。两者都围绕“锁的顺序”展开,不复杂但容易忽略。
统一加锁顺序是最有效的预防手段
多个线程对同一组资源加锁时,只要顺序不一致,就埋下死锁隐患。比如转账操作中,两个账户 A 和 B,线程1按 A→B 加锁,线程2按 B→A 加锁,必然卡住。
- 给所有锁对象定义明确优先级,例如按对象 hashCode() 排序,或按业务 ID 数值大小排序
- 封装加锁逻辑:用一个工具方法统一获取多把锁,内部按固定顺序执行 synchronized 块
- 避免在 synchronized 块内调用外部方法——它可能隐式引入新锁,打乱预设顺序
用 try-finally + 超时机制兜底(需换 ReentrantLock)
synchronized 本身不支持超时或中断,所以真正需要响应性保障时,得换成 ReentrantLock。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 用
lock.tryLock(1, TimeUnit.SECONDS)尝试获取锁,失败就释放已持锁并重试/降级 - 每次加锁后必须配对
unlock(),且放在 finally 块里,防止异常跳过释放 - 这不是放弃 synchronized,而是针对高风险场景(如含远程调用、数据库操作的临界区)做升级
jstack 是最直接的排查工具
线上服务卡住但无报错时,第一反应就是抓线程快照。jstack 能自动识别 Java 层死锁,并高亮关键信息。
- 先执行
jps -l找到 Java 进程 PID - 再运行
jstack -l <pid> > deadlock.log</pid>,-l 参数不能省,否则看不到谁持有哪些锁 - 打开日志,直接翻到末尾找 “found one Java-level deadlock” 区块,它会列出:
- 哪些线程参与死锁
- 谁在等哪个锁地址(如
waiting to lock) - 该锁正被哪个线程持有(如
held by "thread-1") - 每个线程阻塞在源码哪一行(精确到 .java 文件和行号)
从代码结构上减少嵌套锁风险
很多死锁不是故意设计的,而是层层调用中无意叠加了 synchronized 块。
- 避免在一个 synchronized 方法里再调用另一个 synchronized 方法,尤其跨类时
- 把大同步块拆成小粒度操作,只锁真正共享的变量,非必要不锁整个对象
- 区分读写场景:纯读操作尽量不用锁,或改用读写锁(ReentrantReadWriteLock)
- 慎用
synchronized(this)或静态同步方法——它们锁范围大、边界模糊,容易和其他模块冲突
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










