哲学家每天除了思考就是吃饭,假设一张桌子上坐了5名哲学家,他们吃饭时都必须获取两根筷子,并且他们都有着相同的习惯,先拿起左边的筷子再拿起有右边的筷子,如果获取不到两根筷子就等待。 代码演示死锁情况:
public class DiningPhilosophers{ public static class Philosopher implements Runnable { private Object leftChopstick; private Object rightChopstick; public Philosopher(Object leftChopstick, Object rightChopstick) { this.leftChopstick = leftChopstick; this.rightChopstick = rightChopstick; } @Override public void run() { try { while (true) { doAction("思考生命、宇宙......"); synchronized (leftChopstick) { doAction("拿起左边的筷子"); synchronized (rightChopstick){ doAction("拿起右边的筷子 -- 开始吃饭"); doAction("放下右边的筷子"); } doAction("放下左边的筷子"); } } } catch (InterruptedException e) { e.printStackTrace(); } } public void doAction(String action) throws InterruptedException { System.out.println(Thread.currentThread().getName() + action); Thread.sleep((long) (Math.random() * 10)); } } public static void main(String[] args) { Philosopher[] philosophers = new Philosopher[5]; Object[] chopsticks = new Object[5]; for (int i = 0; i < chopsticks.length; i++) { chopsticks[i] = new Object(); } for (int i = 0; i < philosophers.length; i++) { Object leftChopstick = chopsticks[i]; Object rightChopstick = chopsticks[(i+1)%philosophers.length]; philosophers[i] = new Philosopher(leftChopstick, rightChopstick); new Thread(philosophers[i],"哲学家"+(i+1)+"号").start(); } } }运行结果,发现死锁: 原因分析:5名哲学家都拿起了左边的筷子,但是右边的筷子都被其他哲学家握在手里,造成了循环依赖,每个哲学家永远都在等待右边的筷子,每个哲学家都吃不到饭,程序无法继续运行!
解决的办法很多,比较简单的一种策略是改变其中一位哲学家的拿筷子的顺序,这样就不会形成循环等待,也就解决了发生死锁的情况:
//在上面的代码上进行修改 for (int i = 0; i < philosophers.length; i++) { Object leftChopstick = chopsticks[i]; Object rightChopstick = chopsticks[(i+1)%philosophers.length]; if (i == philosophers.length - 1) {//改变其中一位哲学家拿筷子的顺序,避免死锁 philosophers[i] = new Philosopher(rightChopstick, leftChopstick); }else { philosophers[i] = new Philosopher(leftChopstick, rightChopstick); } new Thread(philosophers[i],"哲学家"+(i+1)+"号").start(); }还有其他很多解决的办法:
餐票(比如5个哲学家,只发放4张餐票,只有拿到餐票的哲学家可以就餐,用完了后将餐票交还)领导调节(隔一段时间进行检测,如果发现死锁,随机指定一个哲学家放下持有的筷子)…