活锁是线程处于runnable状态、不断重试却无效推进;饥饿是线程长期处于waiting/blocked状态、因调度不公而得不到资源。

活锁和饥饿都会让线程“看似在运行,实际没进展”,但成因、表现和影响机制完全不同。关键区别在于:活锁是线程主动反复退让导致无效循环;饥饿是线程被动得不到调度,连执行机会都没有。
活锁:线程在跑,但一直在原地打转
活锁中,线程状态是 RUNNABLE,CPU 占用率可能很高,但业务逻辑始终无法完成。本质是协作策略失败——线程为避免冲突而过度谦让,结果陷入“你退我进、我退你进”的同步震荡。
- 典型场景:两个线程同时尝试更新同一行数据库记录,都检测到冲突 → 都回滚 → 都重试 → 再次冲突 → 循环往复
- 真实类比:两人在窄道迎面相遇,都往同一侧让路,又同时退回,再同时让……动作不停,位置不变
- 常见诱因:无延迟的立即重试、固定重试间隔、缺乏随机退避(backoff)机制
饥饿:线程在等,却一直轮不到上场
饥饿中,线程多数时间处于 WAITING 或 BLOCKED 状态,不消耗 CPU,但长期得不到资源分配。问题根源不在协作逻辑,而在调度不公平或资源分配策略失衡。
- 典型场景:低优先级线程反复申请锁,但高优先级线程持续抢占并快速释放,导致低优先级线程始终插不进队列
- 真实类比:火车站窗口只服务VIP旅客,普通旅客排队数小时仍无法购票
- 常见诱因:非公平锁(如 ReentrantLock 默认构造)、固定优先级调度、无超时机制、无资源配额限制
如何识别和应对
排查时看线程堆栈和资源占用模式更有效:
- 发现大量线程处于 RUNNABLE 状态,但日志反复打印“重试”“冲突”“回滚”,且无业务完成记录 → 倾向活锁
- 发现某些线程长时间停留在 WAITING(如 Object.wait、LockSupport.park),且堆栈中锁等待链稳定不变化 → 倾向饥饿
- 活锁缓解:引入随机退避(如 Thread.sleep(50 + new Random().nextInt(100)))、设置最大重试次数、改用乐观锁减少冲突
- 饥饿缓解:使用公平锁(new ReentrantLock(true))、为低优先级任务预留执行窗口、监控线程等待时长并告警










