死锁、活锁、饥饿是多线程活跃性问题的三种典型表现:死锁是线程互相持有并等待对方资源而永久阻塞;活锁是线程不断重试却无实质进展;饥饿是线程长期得不到资源分配而无法执行。

多线程中的“活跃性危险”,指线程看似在运行,却无法取得实际进展——它没崩溃、没抛异常,但就是卡住了、慢了、或干脆被晾在一边。死锁、活锁、饥饿是三种典型表现,本质都是线程“动不了”,但动不了的原因和形态完全不同。
死锁:互相掐住脖子,谁也不松手
两个或多个线程各自持有对方需要的锁,又都在等对方先释放,结果全部永久阻塞。它满足四个硬性条件:资源互斥、持有并等待、不可剥夺、循环等待——缺一不可。
常见场景包括:
- 嵌套加锁顺序不一致:线程A按lock1→lock2加锁,线程B按lock2→lock1加锁
- 单线程内重复获取不可重入锁(如自定义非可重入锁中synchronized(this)里再synchronized(this))
- 数据库事务中跨表更新顺序混乱,引发底层行锁/表锁循环等待
活锁:太有礼貌,反而一事无成
线程没有阻塞,一直在运行、响应、重试,但因策略不当,所有动作都白费——就像两人迎面走来,反复侧身避让却总撞上,谁也过不去。
典型例子有:
- 消息队列中失败消息被立即重投到队首,反复处理失败,形成无限循环
- 多个线程竞争同一把锁,每次刚抢到就发现条件不满足,立刻释放并重试,导致CPU空转
- 网络冲突重传机制未引入随机退避,多个节点同步重发,持续碰撞
关键识别点:线程CPU占用高、日志里重复出现“重试”“回滚”“放弃获取”,但业务状态毫无推进。
饥饿:一直排队,永远轮不到
某个线程长期得不到所需资源(CPU时间片、锁、I/O句柄等),不是因为被锁死,而是被持续压制或忽略。它可能某天突然跑起来,但没人知道要等多久。
常见诱因包括:
- 过度依赖线程优先级:高优先级线程持续抢占,低优先级线程几乎无法调度
- 不公平锁滥用:ReentrantLock默认非公平,新线程总插队,老等待线程迟迟拿不到锁
- 长任务霸占共享资源:一个线程持锁执行耗时操作(如IO、复杂计算),其他线程无限期等待
与死锁的区别在于:饥饿线程仍处于RUNNABLE或BLOCKED状态,系统未卡死,只是分配严重失衡。










