关于哲学家就餐问题的完整解答可参见:
里面列出了3种解法:
- 服务生解法
- 资源分级解法
- Chandy/Misra solution
这里会陆续给出3种解决方案
服务生解法
一个简单的解法是引入一个餐厅服务生,哲学家必须经过他的允许才能拿起餐叉。因为服务生知道哪只餐叉正在使用,所以他能够作出判断避免死锁。
为了演示这种解法,假设哲学家依次标号为A至E。如果A和C在吃东西,则有四只餐叉在使用中。B坐在A和C之间,所以两只餐叉都无法使用,而D和E之间有一只空余的餐叉。假设这时D想要吃东西。如果他拿起了第五只餐叉,就有可能发生死锁。相反,如果他征求服务生同意,服务生会让他等待。这样,我们就能保证下次当两把餐叉空余出来时,一定有一位哲学家可以成功的得到一对餐叉,从而避免了死锁
我感觉就是,引入仲裁,当且仅当可以吃的时候他才拿起筷子,否则等待
package tmp;
public class Main {
public static void main(String[] args) {
Fork fork = new Fork();
for(int i=0 ; i<5 ; ++i)
(new Philosopher(i, fork)).start();
}
}
class Fork{
private boolean[] used = new boolean[]{false,false,false,false,false};
public synchronized void take(int i) throws InterruptedException{
while (used[i] || used[(i+1)%5]) {
System.out.printf("I'm philosopher %d, wait for used[%d] =%d used[%d]=%d\n", i,i,used[i]?1:0,(i+1)%5,used[(i+1)%5]?1:0);
wait();
}
used[i] = true;
used[(i+1)%5] = true;
}
public synchronized void put(int i){
used[i] = false;
used[(i+1)%5] = false;
notifyAll();
}
}
class Philosopher extends Thread{
private int id;
private Fork fork;
public Philosopher(int id, Fork fork) {
super();
this.id = id;
this.fork = fork;
}
private void eat() throws InterruptedException {
int time =(int) (Math.max(0.5, Math.random()) * 2000);//至少1秒
System.out.printf("philosopher %d is eating for %d mils\n",id,time);
this.sleep(time);
}
private void think() throws InterruptedException {
int time =(int) (Math.max(0.5, Math.random()) * 5000);//至少1秒
System.out.printf("philosopher %d is eating for %d mils\n",id,time);
this.sleep(time);
}
public void run() {
while (true) {
try {
think();
fork.take(id);
eat();
fork.put(id);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
显然不会引起死锁,因为不会发生循环等待,也即没有哲学家会在饥饿的同时拿起一只筷子