Java多线程中的死锁问题与避免方法
发布时间 - 2026-01-08 00:00:00 点击率:次死锁典型模式是多线程以不同顺序获取同一组锁,如线程A先锁accountA再锁accountB,线程B反之;常用tryLock()超时避免、统一锁顺序预防、jstack和ThreadMXBean排查。
死锁发生的典型代码模式
Java中死锁最常出现在多个线程以不同顺序获取同一组 Object 锁(或 synchronized 块)时。比如线程 A 先锁 accountA 再尝试锁 accountB,而线程 B 正好相反——这种交叉加锁是死锁的直接诱因。
常见错误现象包括:程序卡住无响应、CPU 占用率低、线程状态长期为 BLOCKED(可通过 jstack 查看线程堆栈,若看到两个以上线程互相等待对方持有的锁,基本可确认)。
- 只在同步块内调用可能阻塞或依赖其他锁的方法,极易引入隐式锁依赖
- 使用
ReentrantLock时调用lock()而非tryLock(long, TimeUnit),失去超时控制能力 - 数据库事务 + JVM 锁混合使用时,JDBC 连接持有和
对象锁顺序不一致,也会触发跨层死锁
如何用 tryLock() 主动规避死锁
ReentrantLock.tryLock(long, TimeUnit) 是最实用的防御手段——它允许你设定等待上限,失败后可释放已持锁并重试或回退,打破循环等待条件。
ReentrantLock lockA = new ReentrantLock();
ReentrantLock lockB = new ReentrantLock();
void transfer(Account from, Account to, BigDecimal amount) {
while (true) {
if (lockA.tryLock(100, TimeUnit.MILLISECONDS) &&
lockB.tryLock(100, TimeUnit.MILLISECONDS)) {
try {
from.withdraw(amount);
to.deposit(amount);
return;
} finally {
lockA.unlock();
lockB.unlock();
}
} else {
// 至少一个锁没拿到,先释放已获得的锁,避免僵持
if (lockA.isHeldByCurrentThread()) lockA.unlock();
if (lockB.isHeldByCurrentThread()) lockB.unlock();
Thread.sleep(10); // 避免忙等
}
}
}
注意:tryLock() 不支持公平锁的“排队语义”,且必须严格配对 unlock(),否则会导致锁泄漏。
统一锁顺序是成本最低的预防方式
如果所有线程都按相同规则获取锁(例如总是先锁 ID 小的对象),就能从根源上消除循环等待。这不需要额外 API,只需约定和校验。
使用场景:账户转账、资源池分配、树形结构遍历等存在天然可比较标识的场景。
- 对锁对象实现
Comparable,或提取唯一可比字段(如account.getId()) - 加锁前先排序:
List—— 用System.identityHashCode()是安全兜底,但不保证跨 JVM 一致,仅限单 JVM 内有效 - 避免在锁内做任何可能引发新锁竞争的操作(如调用外部服务、访问 synchronized 集合)
排查与监控不能只靠日志
死锁往往在压测或上线后才暴露,仅靠业务日志很难定位。必须结合 JVM 自带机制和轻量级埋点。
关键操作:
- 启动参数加入
-XX:+PrintConcurrentLocks -XX:+PrintGCDetails,配合jstack -l可输出显式锁持有关系 - 定期调用
java.lang.management.ThreadMXBean.findDeadlockedThreads()做主动探测(建议封装为健康检查端点) - 对高风险同步块添加计时日志:
long start = System.nanoTime(); ... log.warn("sync block took {}ms", (System.nanoTime()-start)/1_000_000);,持续偏高的耗时是潜在死锁前兆
真正难处理的是“锁链过长”:不是两把锁互等,而是 A→B→C→D→A 形成环路,这种靠人工 review 几乎不可控,必须依赖工具链自动分析锁获取路径。
# java
# js
# 工具
# 栈
# ai
# java多线程
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
最好的网站制作公司,网购哪个网站口碑最好,推荐几个?谢谢?
如何在阿里云完成域名注册与建站?
如何在IIS中配置站点IP、端口及主机头?
Python自然语言搜索引擎项目教程_倒排索引查询优化案例
Laravel定时任务怎么设置_Laravel Crontab调度器配置
中国移动官方网站首页入口 中国移动官网网页登录
韩国网站服务器搭建指南:VPS选购、域名解析与DNS配置推荐
Laravel如何配置Horizon来管理队列?(安装和使用)
Bootstrap整体框架之JavaScript插件架构
Laravel Facade的原理是什么_深入理解Laravel门面及其工作机制
标准网站视频模板制作软件,现在有哪个网站的视频编辑素材最齐全的,背景音乐、音效等?
如何在IIS管理器中快速创建并配置网站?
php结合redis实现高并发下的抢购、秒杀功能的实例
Laravel如何使用Eloquent ORM进行数据库操作?(CRUD示例)
简单实现Android验证码
在centOS 7安装mysql 5.7的详细教程
Laravel事件监听器怎么写_Laravel Event和Listener使用教程
家族网站制作贴纸教程视频,用豆子做粘帖画怎么制作?
微信小程序 input输入框控件详解及实例(多种示例)
青岛网站建设如何选择本地服务器?
香港服务器WordPress建站指南:SEO优化与高效部署策略
佐糖AI抠图怎样调整抠图精度_佐糖AI精度调整与放大细化操作【攻略】
高防服务器租用如何选择配置与防御等级?
Laravel怎么配置.env环境变量_Laravel生产环境敏感数据保护与读取【方法】
Laravel队列任务超时怎么办_Laravel Queue Timeout设置详解
Laravel怎么使用artisan命令缓存配置和视图
laravel怎么在请求结束后执行任务(Terminable Middleware)_laravel Terminable Middleware请求结束任务执行方法
Laravel如何配置和使用缓存?(Redis代码示例)
Linux虚拟化技术教程_KVMQEMU虚拟机安装与调优
如何利用DOS批处理实现定时关机操作详解
简单实现Android文件上传
制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?
Linux系统命令中tree命令详解
,网页ppt怎么弄成自己的ppt?
微信公众帐号开发教程之图文消息全攻略
Laravel如何实现事件和监听器?(Event & Listener实战)
java获取注册ip实例
Gemini手机端怎么发图片_Gemini手机端发图方法【步骤】
Laravel如何自定义分页视图?(Pagination示例)
MySQL查询结果复制到新表的方法(更新、插入)
如何快速配置高效服务器建站软件?
如何在IIS中新建站点并配置端口与物理路径?
iOS验证手机号的正则表达式
Laravel如何连接多个数据库_Laravel多数据库连接配置与切换教程
如何在万网ECS上快速搭建专属网站?
Laravel怎么实现支付功能_Laravel集成支付宝微信支付
如何快速查询网站的真实建站时间?
如何快速生成专业多端适配建站电话?
Thinkphp 中 distinct 的用法解析
如何用腾讯建站主机快速创建免费网站?
上一篇:电脑开机一直卡在欢迎界面怎么办
上一篇:电脑开机一直卡在欢迎界面怎么办


对象锁顺序不一致,也会触发跨层死锁