首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 平面设计 > 图形图像 >

哲学家有关问题(图形化实现)

2012-11-06 
哲学家问题(图形化实现)记得大二时学操作系统,有个关于锁的经典模型——哲学家模型。当时老师要求我们用程序

哲学家问题(图形化实现)
记得大二时学操作系统,有个关于锁的经典模型——哲学家模型。当时老师要求我们用程序实现,我当时没做出来,一直耿耿于怀。今天看Java?cookbook中的线程介绍。于是动手来试下,花了一天才弄出来。在此把大概过程罗列如下,也算是交几年前的作业吧。

?

程序的设计分两部分:哲学家问题的逻辑处理、图形化展示结果部分。最终效果如下:

哲学家有关问题(图形化实现)

图1:初始时五个哲学家都在睡眠

哲学家有关问题(图形化实现)

图2::哲学家0、3在睡眠,1、4在吃饭,2挨饿

哲学家有关问题(图形化实现)

图3:哲学家0、3在吃饭,1在挨饿,2、4在睡眠

首先做一点约定:

对于哲学家按顺时针计算,最顶部的哲学家为0,右边的为1,右下角为2……。

对于筷子见图1,哲学家0、1夹着的筷子为0,按顺时针递增,即1、2中间的筷子为1……

?

至于什么是哲学家问题,若忘记了可以翻阅下操作系统温习下,简单的描述下:有五个哲学家要吃饭,同时只有五个筷子,为了吃饭必须同时获得两个筷子。如果每个哲学家都是先拿左边筷子再拿右边筷子,则必然发生死锁。所以规定了奇数的先拿左边,偶数的先拿右边。由此可见,筷子是竞争资源。准确的讲,

筷子0是哲学家0、1竞争的资源;

筷子1是哲学家1、2竞争的资源;

筷子2是哲学家2、3竞争的资源;

筷子3是哲学家3、4竞争的资源;

筷子4是哲学家4、0竞争的资源;

?

类图如下:

哲学家有关问题(图形化实现)

所有的逻辑都在Person中完成。Person是一个继承了Thread的线程。在PhilosopherDining的main方法中实例化了五个Person并start了他们。现在我们看下Person的run方法及其涉及的其他方法:

/*** 不断的睡眠、吃饭.* * @see java.lang.Thread#run()*/public void run() {   while (!done) {   sleeping();   eat();  }}/*** 睡眠几秒钟.*/public void sleeping() {try {      // id睡觉    guiMgr.setPersonSleeping(id);      System.out.println("Person[" + id + "] is sleeping...");      Thread.sleep(SLEEP_TIME);    } catch (InterruptedException e) {      e.printStackTrace();  }}/*** 吃饭,必须同时得到左右筷子才能吃.*/public void eat() {  // 奇数的先拿左边  if (isOdd(id)) {      // 先拿左边    getChopstick(leftNo);      // 再拿右边    getChopstick(rightNo);  } else {      // 偶数的先拿右边    // 先拿右边    getChopstick(rightNo);      // 再拿左边    getChopstick(leftNo);  }  // 两边都拿到的话则可以放心的吃东西了  eating();}

?

很简单,先睡眠几分钟,饿醒了就吃东西。吃东西又需要先拿左/右边的筷子再拿右/左边的筷子,然后才能吃。关键就在拿筷子getChopstick、吃饭eating这两件事上。

/*** 试图获取筷子,若被他人先拿则需等待.* * @param chopstickNo*            要拿的筷子号*/private void getChopstick(final int chopstickNo) {    System.out.println("id:" + id + " no:" + chopstickNo);    synchronized (chopsticks[chopstickNo]) {      while (chopsticks[chopstickNo].isUsed()) {          System.out.println("Person[" + id + "] is waiting  Chopstick["              + chopstickNo + "]");          try {               // 吃不到东西,哭了          guiMgr.setPersonCrying(id);               chopsticks[chopstickNo].wait();          } catch (InterruptedException e) {               e.printStackTrace();          }      }    }    // 获取得到筷子了  guiMgr.setChopstickImage(id, chopstickNo);    // 占用筷子  chopsticks[chopstickNo].setUsed(true);   System.out.println("Person[" + id + "] has got Chopstick["       + chopstickNo + "] at time:" + new Date());}

?

请先忽略所有与guiMgr相关的代码,那是为此程序加上GUI界面的代码,与逻辑没有任何关系。

首先某个特定的筷子是竞争资源,只有它未被占用时才可持有,也就是上面的while语句。如果一直被Used,则一直wait。直到被notifyAll则可继续下面的代码,即把筷子占用chopsticks[chopstickNo].setUsed(true);

?

接下来是吃的动作

/*** 吃几秒钟.*/private void eating() {    System.out.println("Person[" + id + "] is eating using Chopsticks["        + leftNo + "][" + rightNo + "]");    try {         // 得到了两只筷子,可以吃东西了      guiMgr.setPersonEating(id);         Thread.sleep(SLEEP_TIME);    } catch (InterruptedException e) {        e.printStackTrace();    }    // 吃完释放左右筷子   synchronized (chopsticks[leftNo]) {        // 释放筷子     guiMgr.releaseChopstick(leftNo);       chopsticks[leftNo].setUsed(false);        // 唤醒其他需要此筷子的哲学家     chopsticks[leftNo].notifyAll();    }    synchronized (chopsticks[rightNo]) {      // 释放筷子    guiMgr.releaseChopstick(rightNo);      chopsticks[rightNo].setUsed(false);      chopsticks[rightNo].notifyAll();    }}

?

吃饭大概几秒钟时间,然后把左右筷子释放,并唤醒等待该筷子的其他哲学家。至此,哲学家问题就搞定了。接下来看看GUI图像实现部分。

?

本例采用了组合的方式即PhilosopherDining中含有GuiManage的引用。GuiManage是专门用于转换哲学家问题到GUI图像显示的中间类,它又含有图像实现类JFrameGridLayout的引用,根据PhilosopherDining中Person实例传递的参数做判断,调用JFrameGridLayout暴露出的方法来控制GUI界面。从而剥离了业务逻辑与前端显示的耦合。

大体思路如下。我使用的是GridLayout,即网格布局。

布局的各组件如下。其中最外层是数字,表示坐标,灰色部分才是真正的布局区域,P表示哲学家,C表示筷子。

P0~P4分布在五个相对对称的位置;

筷子的位置因为会经常变化,所以使用复合数字及不同底色表示。其中

红色表示筷子的初始位置,即每个筷子未被任何人持有的情况下的位置;

蓝色表示筷子相对于其初始位置的右边;

黄色表示筷子相对于其初始位置的左边;

哲学家有关问题(图形化实现)

?

这时再看GuiManage中的下面常量就好理解了

/*** 人的位置,固定的.*/private static final Position[] personPositions = new Position[MAX];static {personPositions[0] = new Position(0, 5);personPositions[1] = new Position(4, 10);personPositions[2] = new Position(9, 9);personPositions[3] = new Position(9, 1);personPositions[4] = new Position(4, 0);}/*** 筷子位置,枚举了筷子三种状态的位置. */private static final ChopstickPosition[] chopstickPositions = new ChopstickPosition[MAX];static {// 枚举五个筷子的十五种状态下对应的位置chopstickPositions[0] = new ChopstickPosition(new int[][] { { 1, 6 }, { 3, 9 }, { 3, 7 } });chopstickPositions[1] = new ChopstickPosition(new int[][] { { 5, 9 }, { 8, 9 }, { 6, 7 } });chopstickPositions[2] = new ChopstickPosition(new int[][] { { 9, 8 }, { 9, 2 }, { 7, 5 } });chopstickPositions[3] = new ChopstickPosition(new int[][] { { 8, 1 }, { 5, 1 }, { 6, 3 } });chopstickPositions[4] = new ChopstickPosition(new int[][] { { 3, 1 }, { 1, 4 }, { 3, 3 } });

?

还有这段

/** * 根据筷子号码,及被谁持有设置筷子图片. *  * @param id *            人的id * @param no *            筷子号码*/public void setChopstickImage(final int id, final int no) {    Position p = null;    String image = null;    if (id % MAX == no) {        p = chopstickPositions[no].getPosition(RIGHT_INDEX);        image = getChopstickImage(no, RIGHT_INDEX);    } else {        p = chopstickPositions[no].getPosition(LEFT_INDEX);        image = getChopstickImage(no, LEFT_INDEX);    }    setChopstickImage(no, p, image);}

??

id?%?MAX?==?no则是相对于筷子原位置的右边,这是很好理解的。如现在是哲学家0持有0号筷子则0?%?5?==?0是成立的,则0号筷子处于C00?即(1,6)的位置,(1,6)则为chopstickPositions[0]的第0(RIGHT_INDEX的值)个元素,反正则是C01,这则是chopstickPositions[0]的第1(LEFT_INDEX的值)个元素。

理解了此处则整个画图程序也就基本理解了。

注:C00中第一个数字表示第0号筷子,第二个数字表示是RIGHT_INDEX、LEFT_INDEX或INIT_INDEX的其中一个值。

?

?

热点排行