`
luzitian
  • 浏览: 26673 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

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

阅读更多

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

 

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

0

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

1

2::哲学家03在睡眠,14在吃饭,2挨饿

3:哲学家03在吃饭,1在挨饿,24在睡眠

首先做一点约定:

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

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

 

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

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

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

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

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

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

 

类图如下:

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

/**
* 不断的睡眠、吃饭.
* 
* @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的引用,根据PhilosopherDiningPerson实例传递的参数做判断,调用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]的第0RIGHT_INDEX的值个元素,反正则是C01,这则是chopstickPositions[0]的第1LEFT_INDEX的值)个元素。

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

注:C00中第一个数字表示第0号筷子,第二个数字表示是RIGHT_INDEXLEFT_INDEXINIT_INDEX的其中一个值。

 

 

  • 大小: 37.5 KB
  • 大小: 36 KB
  • 大小: 36.9 KB
  • 大小: 29.6 KB
  • 大小: 24.9 KB
分享到:
评论

相关推荐

    哲学家进餐的设计与实现

    使用c++图形界面做的哲学家进餐问题,完全没问题的项目代码

    进程同步互斥--不死锁哲学家问题

    进程同步互斥——不死锁哲学家问题 java实现。计算机系统原理,课程设计,(1)利用进程并发执行原理,采用奇数号哲学家先拿左叉子,偶数号哲学...(2)利用java中Swing技术将哲学家就餐的活动过程用可视图形化界面表示出来

    哲学家进餐问题仿真

    计算机仿真的实验,用三种方法模拟了哲学家进餐的过程,图形化的显示过程,用VC++.net开发的

    人工智能对文化的影响.pdf

    对于⼈的观念(更具体地指⼈的精神)和机器的观念(更具体地指⼈⼯智能)之间的关系问题,哲学家、 神学家和其它⼈们之间⼀直存在着争论。按照⼈⼯智能的观点,⼈类有可能⽤机器来规划⾃⼰的未来,甚⾄ 可以把这个规划...

    Java开发实战1200例(第1卷).(清华出版.李钟尉.陈丹丹).part3

    实例145 哲学家就餐问题 189 实例146 使用信号量实现线程同步 190 实例147 使用原子变量实现线程同步 191 实例148 使用事件分配线程更新Swing控件 193 实例149 使用SwingWorker类完成耗时操作 194 第7章 反射与异常...

    人工智能专业课程是什么.pdf

    ⼈⼯智能平台与⼯具课程群 具体课程:《群体智能与⾃主系统》《⽆⼈驾驶技术与系统实现》《游戏设计与开发》《计算机图形学》《虚拟现实与增 强现实》 6.⼈⼯智能核⼼课程群 具体课程:《⼈⼯智能的现代⽅法I》...

    人工智能行业发展概况.pptx

    人工智能(Artificial Intelligence): 是指使用机器代替人类实现认知、识别、分析、决策等功 能,其本质是对人的意识与思维的信息过程的模拟,是一 门综合了计算机科学、生理学、哲学的交叉学科。凡是使 用机器...

Global site tag (gtag.js) - Google Analytics