转自:http://blog.csdn.net/tayanxunhua/article/details/38691005
死锁经典案例:哲学家就餐。
这个案例会导致死锁。
通过修改《Java编程思想4》一书中的案例,来做实验,代码更易理解,结果也相对容易控制。
附代码:
筷子类:
1 package com.tyxh.ch21.c6; 2 3 public class Chopstick { 4 private boolean taken = false;//判断是此筷子是否被拿起 5 public synchronized void take() throws InterruptedException { 6 while(taken) { 7 //如果已被拿起,则等待 8 wait(); 9 } 10 //如果没有被拿起,则可以被拿起,并设置taken为true 11 taken = true; 12 } 13 14 public synchronized void drop() { 15 //放下筷子之后设置taken为false,并通知其他哲学家 16 taken = false; 17 notifyAll(); 18 } 19 }
哲学家类:
1 package com.tyxh.ch21.c6; 2 3 import java.util.Random; 4 import java.util.concurrent.TimeUnit; 5 6 public class Philosopher implements Runnable { 7 private Chopstick left;//左筷子 8 private Chopstick right;//右筷子 9 10 private final int id;//哲学家编号 11 private final int ponderFactor;//根据这个属性设置思考时间 12 13 private Random rand = new Random(47); 14 private void pause() throws InterruptedException { 15 if(ponderFactor == 0) { 16 return; 17 } 18 TimeUnit.MILLISECONDS.sleep(rand.nextInt(ponderFactor *250)); 19 } 20 21 public Philosopher(Chopstick left, Chopstick right, int ident, int ponder) { 22 this.left = left; 23 this.right = right; 24 this.id = ident; 25 this.ponderFactor = ponder; 26 } 27 28 public void run() { 29 try{ 30 while(!Thread.interrupted()) { 31 System.out.println(this + " " + "thinking"); 32 pause(); 33 right.take(); 34 System.out.println(this + " " + "拿右筷子"); 35 left.take(); 36 System.out.println(this + " " + "拿左筷子"); 37 pause(); 38 System.out.println(this + " " + "吃"); 39 right.drop(); 40 System.out.println(this + " " + "放下右筷子"); 41 left.drop(); 42 System.out.println(this + " " + "放下左筷子"); 43 } 44 }catch(InterruptedException e) { 45 System.out.println(this + " 退出 "); 46 } 47 } 48 49 public String toString() { 50 return "Phiosopher : " + id; 51 } 52 }
测试类:
1 package com.tyxh.ch21.c6; 2 3 import java.util.concurrent.ExecutorService; 4 import java.util.concurrent.Executors; 5 import java.util.concurrent.TimeUnit; 6 7 public class DeadlockingDiningPhilosophers { 8 public static void main(String[] args) throws InterruptedException { 9 int ponder = 5; 10 if(args.length > 0) { 11 ponder = Integer.parseInt(args[0]); 12 } 13 int size = 5; 14 if(args.length > 1) { 15 size = Integer.parseInt(args[1]); 16 } 17 ExecutorService exec = Executors.newCachedThreadPool(); 18 Chopstick[] stick = new Chopstick[size]; 19 20 for(int i = 0; i < size; i++) { 21 stick[i] = new Chopstick(); 22 } 23 24 for(int i = 0; i < size; i++) { 25 Philosopher p = new Philosopher(stick[i], stick[(i+1)%size], i, ponder); 26 exec.execute(p); 27 } 28 29 TimeUnit.SECONDS.sleep(3); 30 exec.shutdownNow(); 31 32 } 33 } 34
可以通过命令行参数调整ponder因子设置哲学家思考时间,也可以设置筷子及哲学家的数量size。
死锁产生的四个必要条件。
1>互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
2>不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
3>请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的战友。
4>循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。
当上述四个条件都成立的时候,便形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失。
这里仅给出书中处理此死锁的解决方案(破坏第四种条件):
方案是:
前面哲学家拿筷子的顺序都是先拿右,再拿左,但最后一个哲学家拿筷子的顺序是先拿左,再拿右,就可以通过阻止循环等待这个死锁的条件来阻止死锁发生。
即将代码:
1 for(int i = 0; i < size; i++) { 2 Philosopher p = new Philosopher(stick[i], stick[(i+1)%size], i, ponder); 3 exec.execute(p); 4 }
修改为:
1 for(int i = 0; i < size; i++) { 2 if(i < size - 1) { 3 Philosopher p = new Philosopher(stick[i], stick[(i+1)%size], i, ponder); 4 exec.execute(p); 5 }else { 6 Philosopher p = new Philosopher(stick[0], stick[i], i, ponder); 7 exec.execute(p); 8 } 9 }