死锁经典案例:哲学家就餐。
这个案例会导致死锁。
通过修改《Java编程思想4》一书中的案例,来做实验,代码更易理解,结果也相对容易控制。
附代码:
筷子类:
package com.tyxh.ch21.c6;
public class Chopstick {
private boolean taken = false;//判断是此筷子是否被拿起
public synchronized void take() throws InterruptedException {
while(taken) {
//如果已被拿起,则等待
wait();
}
//如果没有被拿起,则可以被拿起,并设置taken为true
taken = true;
}
public synchronized void drop() {
//放下筷子之后设置taken为false,并通知其他筷子
taken = false;
notifyAll();
}
}
哲学家类:
package com.tyxh.ch21.c6;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class Philosopher implements Runnable {
private Chopstick left;//左筷子
private Chopstick right;//右筷子
private final int id;//哲学家编号
private final int ponderFactor;//根据这个属性设置思考时间
private Random rand = new Random(47);
private void pause() throws InterruptedException {
if(ponderFactor == 0) {
return;
}
TimeUnit.MILLISECONDS.sleep(rand.nextInt(ponderFactor *250));
}
public Philosopher(Chopstick left, Chopstick right, int ident, int ponder) {
this.left = left;
this.right = right;
this.id = ident;
this.ponderFactor = ponder;
}
public void run() {
try{
while(!Thread.interrupted()) {
System.out.println(this + " " + "thinking");
pause();
right.take();
System.out.println(this + " " + "拿右筷子");
left.take();
System.out.println(this + " " + "拿左筷子");
pause();
System.out.println(this + " " + "吃");
right.drop();
System.out.println(this + " " + "放下右筷子");
left.drop();
System.out.println(this + " " + "放下左筷子");
}
}catch(InterruptedException e) {
System.out.println(this + " 退出 ");
}
}
public String toString() {
return "Phiosopher : " + id;
}
}
测试类:
package com.tyxh.ch21.c6;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class DeadlockingDiningPhilosophers {
public static void main(String[] args) throws InterruptedException {
int ponder = 5;
if(args.length > 0) {
ponder = Integer.parseInt(args[0]);
}
int size = 5;
if(args.length > 1) {
size = Integer.parseInt(args[1]);
}
ExecutorService exec = Executors.newCachedThreadPool();
Chopstick[] stick = new Chopstick[size];
for(int i = 0; i < size; i++) {
stick[i] = new Chopstick();
}
for(int i = 0; i < size; i++) {
Philosopher p = new Philosopher(stick[i], stick[(i+1)%size], i, ponder);
exec.execute(p);
}
TimeUnit.SECONDS.sleep(3);
exec.shutdownNow();
}
}
可以通过命令行参数调整ponder因子设置哲学家思考时间,也可以设置筷子及哲学家的数量size。
这里仅给出书中处理此死锁的解决方案:
方案是:
前面哲学家拿筷子的顺序都是先拿右,再拿左,但最后一个哲学家拿筷子的顺序是先拿左,再拿右,就可以通过阻止循环等待这个死锁的条件来阻止死锁发生。
即将代码:
for(int i = 0; i < size; i++) {
Philosopher p = new Philosopher(stick[i], stick[(i+1)%size], i, ponder);
exec.execute(p);
}
修改为:
for(int i = 0; i < size; i++) {
if(i < size - 1) {
Philosopher p = new Philosopher(stick[i], stick[(i+1)%size], i, ponder);
exec.execute(p);
}else {
Philosopher p = new Philosopher(stick[0], stick[i], i, ponder);
exec.execute(p);
}
}