哲学家就餐问题
2016-11-29 14:02
295 查看
Title 哲学家就餐问题是在1965年由Dijkstra提出并解决的一个问题,后来成为同步问题的一个经典问题。
Disciber 有五位哲学家围坐在一张桌子前, 他们面前都放了一碗面,彼此间放了一把叉子。因为面非常的滑,以至于使用一个叉子吃不了,所以每位哲学家需要拿起放在他左右两边的叉子吃面。同时,每位哲学家具有两种状态:进餐、思考问题。每当一位哲学家感到饥饿时,他会试图去获得位于他左右两边的那两把叉子,每次都只能拿一把,先后顺序无所谓,两把叉子都拿到后才能开始进餐,吃完了以后,他需要把两把叉子返回原处,然后继续思考。
Question 五个哲学家,五碗面和五个叉子,每碗面需要两个叉子,如何进行操作?
Analysis 1、 这里每位哲学家都具有各自的面,而相互之间竞争的是吃面的叉子,也就是说叉子是共享资源。但他们竞争的叉子也是有限制,也就是当前哲学家左右两边的叉子,而不能是其他叉子。如果进行抽象建模,可以得出如下模型:
所以我们可以采用缓冲区的方式,对叉子的使用进行控制,有权拿叉子的哲学家则分配,无权拿叉子的哲学家则需要阻塞。
Analysis 2、这里的每位哲学家都具有各自的面,而他们的状态是吃面、思考,那么每次都只能让其中的两位吃面,其他的要么在思考,要么在等待。当某个哲学家在吃面时,哪些哲学家可以吃面,哪些要等待。其实就是当前正在吃面的哲学家的左右两边的哲学家不能吃面,要么思考,要么等待,当哲学家吃完,则需要告诉左右两边还在等待的哲学家他们可以申请吃面的权利了。
这里我们采用第二套分析方案:
测试类:
Disciber 有五位哲学家围坐在一张桌子前, 他们面前都放了一碗面,彼此间放了一把叉子。因为面非常的滑,以至于使用一个叉子吃不了,所以每位哲学家需要拿起放在他左右两边的叉子吃面。同时,每位哲学家具有两种状态:进餐、思考问题。每当一位哲学家感到饥饿时,他会试图去获得位于他左右两边的那两把叉子,每次都只能拿一把,先后顺序无所谓,两把叉子都拿到后才能开始进餐,吃完了以后,他需要把两把叉子返回原处,然后继续思考。
Question 五个哲学家,五碗面和五个叉子,每碗面需要两个叉子,如何进行操作?
Analysis 1、 这里每位哲学家都具有各自的面,而相互之间竞争的是吃面的叉子,也就是说叉子是共享资源。但他们竞争的叉子也是有限制,也就是当前哲学家左右两边的叉子,而不能是其他叉子。如果进行抽象建模,可以得出如下模型:
循环: 思考问题; 尝试拿左边叉子; 尝试拿右边叉子; 吃面; 放下左边叉子; 放下右边的叉子;
所以我们可以采用缓冲区的方式,对叉子的使用进行控制,有权拿叉子的哲学家则分配,无权拿叉子的哲学家则需要阻塞。
Analysis 2、这里的每位哲学家都具有各自的面,而他们的状态是吃面、思考,那么每次都只能让其中的两位吃面,其他的要么在思考,要么在等待。当某个哲学家在吃面时,哪些哲学家可以吃面,哪些要等待。其实就是当前正在吃面的哲学家的左右两边的哲学家不能吃面,要么思考,要么等待,当哲学家吃完,则需要告诉左右两边还在等待的哲学家他们可以申请吃面的权利了。
循环: 思考问题; 申请权利; 吃面; 放弃权利; 通知;
这里我们采用第二套分析方案:
import java.io.File; import java.io.FileWriter; public class Philosopher extends Thread { private int state; //表示哲学家的状态,0思考、1表示饥饿和2表示正在吃饭 private Philosopher left; //左边的哲学家 private Philosopher right; //右边的哲学家 private String name; //记录自己的名字 public Philosopher(final String name) { this.name = name; } public int getPState() { return this.state; } public void setPState(final int state) { this.state = state; } public void setLeft(final Philosopher philosopher) { this.left = philosopher; } public Philosopher getLeft(){ return this.left; } public void setRight(final Philosopher philosopher) { this.right = philosopher; } public Philosopher getRight(){ return this.right; } /** * 判断当前是否具备吃的条件. */ public boolean canEat() { //当前哲学家处于饥饿状态,而且左右两边的哲学家还没有一个人处于吃的状态,那么当前哲学家就可以吃 if(this.getPState() == 1 && this.getLeft().getPState() != 2 && this.getRight().getPState() != 2) { return true; } return false; } /** * 哲学家吃饭.... */ public synchronized void eating() { /*ERROR: Exception Current Thread not owner,也就是wait、notify都只能在当前线程中访问,而其他线程不能访问 try { //首先堵住左右两边的哲学家进行竞争 //阻塞左边的哲学家 if(this.getLeft().getPState() == 1) { this.getLeft().wait(); } //阻塞右边的哲学家 if(this.getRight().getPState() == 1) { this.getRight().wait(); } } catch (Exception e) { e.printStackTrace(); } */ this.setPState(2); //将当前哲学家的状态置为2,从而阻塞左右两边的线程 //阻塞左边的哲学家 if(this.getLeft().getPState() == 1) { this.getLeft().PEat(); } //阻塞右边的哲学家 if(this.getRight().getPState() == 1) { this.getRight().PEat(); } System.out.println(this.name + " 正在吃饭...."); try{ Thread.sleep(300); // 吃饭时间0.3秒 } catch(Exception e) { e.printStackTrace(); } System.out.println(this.name + " 吃完了!"); this.setPState(0); // 重新回到思考问题的状态 } /** * 哲学家思考问题.... */ public void thinking() { System.out.println(this.name + "正在思考问题...."); /*ERROR: 这里有问题,因为Thread.sleep不会让出对象锁,那么很可能会造成一个问题 那就是当前对象释放了锁的同时,很快又得到了锁,从而锁住左右两边的对象,陷入死锁。 try{ Thread.sleep(1000); //哲学家思考1秒 } catch(Exception e) { e.printStackTrace(); } */ //这里让哲学家干点事,不然执行太快 /*#WARNING: 想通过写文件的方式,不行。因为CPU执行和文件读写是区分的。 try { File file = new File("./question.txt"); if(!file.exists()) { file.createNewFile(); } FileWriter writer = new FileWriter(file); writer.append(this.name + " 正在思考 1 + 1 = ? \n"); writer.close(); } catch (Exception e) { e.printStackTrace(); } */ double i = 0.0; while (i < 100000000) { i += 0.1; } this.setPState(1); //思考完了以后就饿了 } /** * 给每一个哲学家一个信号量 */ public synchronized void PEat() { if(!this.canEat()) { try { this.wait(); } catch(Exception e) { e.printStackTrace(); } } } public synchronized void VEat() { /*#ERROR: Exception Current Thread not owner,也就是wait、notify都只能在当前线程中访问,而其他线程不能访问 //唤醒左边的哲学家 if(this.getLeft().getPState() == 1) { this.getLeft().notify(); } //唤醒右边的哲学家 if(this.getRight().getPState() == 1) { this.getRight().notify(); } */ this.notify(); } public void run() { while(true) { thinking(); this.PEat(); this.eating(); //唤醒左边的哲学家 if(this.getLeft().getPState() == 1) { this.getLeft().VEat(); } //唤醒右边的哲学家 if(this.getRight().getPState() == 1) { this.getRight().VEat(); } } } }
测试类:
public class PhilosopherEatingQuestion { public static void main(String[] args) { /** * 任意时刻都只能有两个对象在吃饭。 */ Philosopher pher1 = new Philosopher("Lao Tzu"); Philosopher pher2 = new Philosopher("Confucius"); Philosopher pher3 = new Philosopher("Plato"); Philosopher pher4 = new Philosopher("Aristotle"); Philosopher pher5 = new Philosopher("Decare"); //现在来排位置 pher1.setLeft(pher5); pher1.setRight(pher1); pher2.setLeft(pher1); pher2.setRight(pher3); pher3.setLeft(pher2); pher3.setRight(pher4); pher4.setLeft(pher3); pher4.setRight(pher5); pher5.setLeft(pher4); pher5.setRight(pher1); // 启动 pher1.start(); pher2.start(); pher3.start(); pher4.start(); pher5.start(); } }
相关文章推荐
- 【经典问题】现代操作系统经典问题回顾(哲学家就餐问题C#实现)
- 哲学家就餐问题的C#实现
- C语言解决哲学家就餐问题
- 哲学家就餐问题
- 哲学家就餐问题
- 【经典操作系统问题】哲学家就餐问题分析
- 进程同步的经典问题2——哲学家就餐问题
- VC多线程对哲学家就餐问题的图像界面动态实现
- 哲学家就餐问题的一个解法
- JavaSE第一百零四讲:哲学家就餐问题、死锁与使用wait及notify方法实现线程之间的相互通信
- 算法讨论:哲学家就餐问题
- 哲学家就餐的问题--java实现
- 哲学家就餐问题在 linux 上的程序实现
- linux下 多线程编程 哲学家就餐问题
- 哲学家就餐问题实现--多线程同步(unix - windows)
- [转]算法讨论:哲学家就餐问题
- 多线程模拟哲学家就餐问题
- 哲学家就餐的问题--java实现
- 哲学家就餐问题的C#实现
- [Unix系统编程]用信号量实现哲学家就餐问题