活锁是线程处于RUNNABLE状态、CPU占用高但任务原地打转;典型原因包括互相响应式退让、无延迟忙等待、确定性重试及协作逻辑缺陷;区别于死锁在于线程不阻塞而持续循环;应对需引入随机退避、最大重试限制、条件等待及职责分离。

活锁不是线程卡死,而是“忙得没进展”——线程始终处于 RUNNABLE 状态,CPU 占用高,但任务反复尝试、失败、再尝试,原地打转。
活锁的典型出现条件
它不满足死锁的“阻塞”特征,却同样导致业务停滞。关键在于:线程未被挂起,却因过度谦让或响应式重试陷入无效循环。
- 互相响应式行为:比如两个线程都检测到对方“正在操作”,于是主动退让、释放资源、重试,结果彼此同步退让,谁也不先动
- 无延迟的忙等待:轮询检查某个条件(如资源可用、消息就绪),失败后立刻重试,不休眠、不让出 CPU
-
确定性重试策略:多个线程在相同时机、相同逻辑下重复申请同一资源(如同时调用
tryLock()失败后立即重试) - 协作逻辑缺陷:例如事务回滚后无差别重试,而失败原因未消除(如数据冲突、格式错误),导致无限循环
和死锁最直观的区别
看线程状态和 CPU 表现:
- 死锁线程是 BLOCKED 或 WAITING,jstack 显示“waiting for monitor entry”,CPU 使用率低
- 活锁线程是 RUNNABLE,jstack 显示仍在执行某段代码(如 while 循环、sleep 前的判断),CPU 持续飙升
实用的处理方式
核心思路是打破“确定性同步响应”,引入异步、延迟或随机性,让至少一个线程能“抢跑”成功。
立即学习“Java免费学习笔记(深入)”;
-
加随机退避:重试前用
Thread.sleep(new Random().nextInt(100)),避免多线程整齐划一地撞车 - 设置最大重试次数:对消息处理、事务重试等场景,超限后转入死信队列或人工干预,不盲目循环
-
改忙等待为条件等待:用
wait()/notify()或Condition.await()替代 while + sleep,让线程真正挂起并由事件唤醒 - 分离“检测”与“执行”职责:比如用单独的协调线程判断是否可执行,工作线程只听指令,避免各自独立决策引发冲突
基本上就这些。活锁比死锁更隐蔽,但只要留意高 CPU + 无业务日志 + 线程持续 RUNNABLE 的组合,再结合代码里有没有“反复检查—失败—重试”模式,就能快速定位。










