《用哲学家就餐问题解释死锁》
场景还原
哲学家D
🥢5 🥢1
哲学家C 哲学家A
🥢4 🥢2
哲学家B
🥢3
剧情设定:
5个秃头程序员(哲学家)转行做厨师,必须同时拿到左右两边的筷子(服务器资源)才能干饭(处理请求)。但筷子数量 == 哲学家数量,引发高并发修罗场。
死锁四要素的爆笑解读
- 互斥:筷子是独占资源 -> "我的筷子你别碰,就像生产环境的Redis锁"
- 占有且等待:拿着左筷等右筷 -> "就像你占着测试环境不释放还抢预发环境"
- 不可抢占:不能抢别人手里的筷子 -> "总不能拔掉同事的网线抢数据库连接吧"
- 循环等待:A等B的筷子,B等C的... -> "像极了跨团队甩锅时的责任闭环"
死亡代码演示(完整可运行)
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class DiningPhilosophers {
// 五根筷子(锁对象)
static Lock[] chopsticks = {
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock(),
new ReentrantLock()
};
public static void main(String[] args) {
// 创建五个哲学家线程
for (int i = 0; i < 5; i++) {
final int philosopher = i;
new Thread(() -> {
while (true) {
// 先拿左边筷子(对5号哲学家来说左边是0号筷子)
chopsticks[philosopher].lock();
System.out.println("哲学家" + philosopher + "拿起左筷" + philosopher);
// 再拿右边筷子(取模处理环形结构)
int right = (philosopher + 1) % 5;
chopsticks[right].lock();
System.out.println("哲学家" + philosopher + "拿起右筷" + right);
// 开始干饭(用随机时间模拟业务处理)
System.out.println("哲学家" + philosopher + "暴风吸入中...");
try { Thread.sleep((long)(Math.random()*1000)); }
catch (InterruptedException e) {}
// 放下筷子
chopsticks[right].unlock();
chopsticks[philosopher].unlock();
}
}).start();
}
}
}
运行结果:
哲学家0拿起左筷0
哲学家1拿起左筷1
哲学家2拿起左筷2
哲学家3拿起左筷3
哲学家4拿起左筷4
(程序卡死,无人能拿到两根筷子)
破局之道:阿里P7的祖传解决方案
方案一:资源排序法(破坏循环等待)
// 修改拿筷子的顺序
if (philosopher % 2 == 0) {
// 偶数编号哲学家先拿左后拿右
pickLeftThenRight();
} else {
// 奇数编号哲学家先拿右后拿左
pickRightThenLeft();
}
方案二:超时放弃(避免无限等待)
if (chopsticks[philosopher].tryLock(500, TimeUnit.MILLISECONDS)) {
if (chopsticks[right].tryLock(500, TimeUnit.MILLISECONDS)) {
// 成功拿到两把筷子
} else {
// 释放已获得的筷子
chopsticks[philosopher].unlock();
}
}
方案三:服务员协调(类似数据库死锁检测)
// 使用一个全局锁作为协调者
Lock butler = new ReentrantLock();
void eat() {
butler.lock();
try {
pickLeftThenRight();
} finally {
butler.unlock();
}
}
面试暴击三连
-
基础题:画图说明死锁四要素如何体现在该场景
(答案示例:用筷子图标注互斥资源与等待环) -
进阶题:如果使用
Synchronized关键字会怎样?
(危险操作:synchronized无法中断,死锁更难检测) -
骚操作题:如何用
jstack检测这段代码的死锁?
(实战演示:运行后使用jstack <pid>查看死锁线程信息)
技术冷笑话精选
- "这段代码就像产品经理的需求——看起来都能跑,实际上谁也动不了"
- "当哲学家开始互相等待,就像你在等前端联调,前端在等测试环境"
- "解决死锁的最好办法:把哲学家变成素食主义者(减少资源竞争)"
下期预告:《IOC容器の千层套路》