哲学家进餐问题的模拟【操作系统】


一、设计目的

死锁是进程并发执行过程中可能出现的现象,所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局。哲学家就餐问题是描述死锁的经典例子。为了防止死锁,可以采用资源预分配法或者资源按序分配法。资源预分配法是指进程在运行前一次性地向系统申请它所需要的全部资源,如果系统当前不能够满足进程的全部资源请求,则不分配资源, 此进程暂不投入运行,如果系统当前能够满足进程的全部资源请求, 则一次性地将所申请的资源全部分配给申请进程。

二、设计内容

哲学家进餐问题的模拟。

三、开发环境

windows环境,Myeclipse平台。

四、分析设计

【1】实验原理

哲学家进餐问题描述的是五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五只碗和五只筷子。他们的生活方式是交替地进行思考和进餐。平时,一个哲学家进行思考,饥饿时便试图取用其左右的最靠近他的筷子,只有在他拿到两只筷子时才能进餐。进餐完毕放下筷子继续思考。

由于:①只有拿到两只筷子时,哲学家才能吃饭;②如果筷子已经在他人手上,则该哲学家必须等到他人吃完之后才能拿到筷子;③任何一个哲学家在自己没有拿到两只筷子吃饭之前,决不放下自己手中的筷子。则可能出现五个哲学家都饥饿时都拿着一直筷子。这样就可能五个哲学家都用不上餐。

该问题可用记录型信号量解决,经分析可知,放在桌子上的筷子是临界资源,在一段时间内只允许一位哲学家使用,为了实现对筷子的互斥使用,可以用一个信号量表示一只筷子,由这五个信号量组成信号量数组。当哲学家饥饿时总是先拿其左边的筷子,成功后,再去拿右边的筷子,又成功后方可就餐。进餐完,又先放下他左边的筷子,再放下右边筷子。这个算法可以保证不会有两个相邻的哲学家同时就餐,但有可能引起死锁。

对于死锁问题可采取这样的几种解决方法:

(1)至多只允许四个哲学家同时进餐,以保证至少有一个哲学家可以进餐,最终总会释放出他所用过的两只筷子,从而可使更多的哲学家进餐;

(2)仅当左右两只筷子均可用时,才允许哲学家拿起筷子就餐

(3)规定奇数号哲学家先拿起右边筷子,然后再去拿左边筷子,而偶数号哲学家则相反。

(4)把筷子顺序编号 0, 1, 2, 3, 4,给每个哲学家分配筷子时,必须依从小号到大号(或者相反顺序)进行。

在本次实习里采用第二种方法解决该问题。

【2】数据及程序结构

总共创建有四个类:哲学家进餐问题类,Philosopher类,ChopstickArray

类,Chopstick类。

Chopstick类来表示筷子,其中包括的布尔型成员变量available来表示该筷子是否可用,成员方法setnum()获取其编号;boolean型成员方法isAvailable()返回其当前available的值。setAvailable(boolean available)这一成员方法是对筷子的available的值进行设置,即设置筷子是否可用。

ChopstickArray类用其中的数组chopsticks[i]来存放五只筷子,并通过哲学家的编号及筷子的编号确定该筷子属于当前哲学家的左右哪边的筷子。

Philosopher类,用来描述哲学家,通过实现Runnable接口的方式来创建线程对象,该类中的方法thinking(),eating()来描述哲学家的状态。通过使用关键词synchronized来给共享资源即Philosopher对象上锁,当一个线问程访问Philosopher中的Thinking()时锁定Philosopher对象,这时其他线程就无法访问其另一个方法eating(),即说明哲学家不能同时处于思考和吃饭的状态中。

public synchronized void thinking()

{

	if(state) // 如果在思考,说明这个哲学家两边的筷子没用 \*/

{

	chopstickArray.getnum(num).setAvailable(**true**);

	chopstickArray.getLast(num).setAvailable(**true**); //这时哲学家两边的筷子只为可用

	String text = thinkingTextArea.getText();

	thinkingTextArea.setText(text+**this** + " 在思考\\n");

try

{

	Thread.sleep(10000);

}catch(Exception e)

{

e.printStackTrace();

}

}

state = **false**; /\*思考完成,进入饥饿状态\*/

**public** **synchronized** **void** eating()

{

**if**(!state) /\* 若不在在思考\*/

{

**if**(chopstickArray.getnum(num).isAvailable()) /\* 若哲学家右手边的筷子可用\*/

{

**if**(chopstickArray.getLast(num).isAvailable())/\*如果左手边的筷子也可用,该哲学家状态设为在吃饭,其两侧的筷子都设置为不可用 \*/

{

chopstickArray.getnum(num).setAvailable(**false**);

chopstickArray.getLast(num).setAvailable(**false**);

String text = eatingTextArea.getText();

eatingTextArea.setText(text+**this** + " 在吃饭\\n");

**try**

{

Thread.*sleep*(10000);

}**catch**(Exception e)

{

e.printStackTrace();

}

}**else**

{ /\* 右手边的筷子可用,但是左手边的不可用\*/

String str = waitingTextArea.getText();

waitingTextArea.setText(str+**this**+" 等待左手边 "+chopstickArray.getLast(num)+"\\n");

**try**

{

wait(**new** Random().nextInt(100));

}**catch**(Exception e)

{

e.printStackTrace();

}

}

}**else**

{ /\* 如果哲学家右手边的筷子不可用则等待\*/

String str = waitingTextArea.getText();

waitingTextArea.setText(str+**this**+" 等待右手边 "+chopstickArray.getnum(num)+"\\n");

**try**

{

wait(**new** Random().nextInt(100));

}**catch**(Exception e)

{

e.printStackTrace();

}

}

}

state = **true**;

哲学家进餐问题类建立一个小程序界面。通过一个“程序开始”的按钮,创建出“筷子”和“哲学家”:

**public** **void** actionPerformed(ActionEvent e)

{

**if**(e.getActionCommand().equals("程序开始"))

{

ChopstickArray chopstickArray = **new** ChopstickArray(5);

/\*五只“筷子”\*/

**for**(**int** i = 0; i \< 5; i++)

{

**new** Thread(**new**Philosopher(i,chopstickArray,

thinkingTextArea,eatingTextArea, waitingTextArea)).start(); /\*创建五个哲学家线程\*/

}

}

}

五、运行示例及结果分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yaT0Kkie-1652198194511)(media/ef59d28ba423c66abe7f6e09cc3eb98b.png)]

首先哲学家0竞争到筷子进入吃饭状态,此时哲学家1和哲学家4无法获得足够筷子,而哲学家3左右筷子可用进入了吃饭状态,此时筷子4、筷子0、筷子2和筷子3不可用。而这时由思考到饥饿的哲学家1和哲学家4只好进入等待状态。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S0xGoN4H-1652198194513)(media/a2a6dfbbd91a1b72057612f34638cce3.png)]

接下来哲学家0和哲学家3吃完饭其左右筷子进入思考,此时哲学家2饥饿竞争到了起左右筷子进入吃饭状态,饿了的哲学家1只好又等待,此时哲学家4左右筷子可用则进入吃饭状态,没等到筷子的哲学家1只好回去继续思考。

六、总结

本次操作系统实习主要对操作系统一些功能块进行了了解,并通过自己设计小型操作系统模块使得我们对操作系统的了解更加深入。实习中还有对LINUX操作系统内核代码的分析,使我们具体的认识了LINUX,了解其设计思想和功能模块,而在LINUX下的各种常用命令也要求我们熟练掌握。通过实习让我们掌握了更多更详细的操作系统的知识,而且通过自己动手模拟演示其功能,体验操作系统的具体执行。而在编写小程序的时候,在同学们和老师的帮助下解决的了很多的困难。而其中对于多线程的编程对于解决并发性问题高效性也在实习中有了深刻的了解。在实习中我不仅学到了很多的知识,还通过查找解决问题的方法认识到解决问题有时需要不仅是一个人的力量,而是一个整体的力量。这些在我们今天的学习工作生活中是很重要的。最后还要多加感谢实习中同学们和老师的提供帮助。

源程序清单:

**import** java.applet.\*;

**import** java.awt.event.\*;

**import** java.awt.\*;

**import** java.util.Random;

**public** **class** 哲学家进餐问题 **extends** Applet **implements** ActionListener,TextListener

{

**private** Button But1;

TextArea thinkingTextArea,eatingTextArea,waitingTextArea;

**public** **void** init()

{

But1 =**new** Button("程序开始");

But1.addActionListener(**this**);

add(But1);

thinkingTextArea = **new** TextArea("思考中:\\n",30, 20);

add(thinkingTextArea);

eatingTextArea = **new** TextArea("吃饭中:\\n",30, 20);

add(eatingTextArea);

waitingTextArea = **new** TextArea("等待中:\\n",30, 28);

add(waitingTextArea);

}

**public** **void** actionPerformed(ActionEvent e)

{

**if**(e.getActionCommand().equals("程序开始"))

{

ChopstickArray chopstickArray = **new** ChopstickArray(5);

**for**(**int** i = 0; i \< 5; i++)

{

**new** Thread(**new** Philosopher(i,chopstickArray,thinkingTextArea,eatingTextArea, waitingTextArea)).start();

}

}

}

**public** **static** **void** main(String[] args)

{

**new** 哲学家进餐问题();

}

**public** **void** textValueChanged(TextEvent e)

{

// **TODO** Auto-generated method stub

}

}

**class** Philosopher **implements** Runnable /\*哲学家类\*/

{

**public** Philosopher(**int** num, ChopstickArray chopstickArray,TextArea thinkingTextArea, TextArea eatingtextArea, TextArea waitingTextArea)

{

**this**.num = num;

**this**.chopstickArray = chopstickArray;

**this**.thinkingTextArea = thinkingTextArea;

**this**.eatingTextArea = eatingtextArea;

**this**.waitingTextArea = waitingTextArea;

}

**public** **synchronized** **void** thinking()

{

**if**(state) /\* 如果在思考,说明这个哲学家两边的筷子没用 \*/

{

chopstickArray.getnum(num).setAvailable(**true**);

chopstickArray.getLast(num).setAvailable(**true**);

String text = thinkingTextArea.getText();

thinkingTextArea.setText(text+**this** + " 在思考\\n");

**try**

{

Thread.*sleep*(10000);

}**catch**(Exception e)

{

e.printStackTrace();

}

}

state = **false**;

}

**public** **synchronized** **void** eating()

{

**if**(!state) /\* 若不在在思考\*/

{

**if**(chopstickArray.getnum(num).isAvailable()) /\* 若哲学家右手边的筷子可用\*/

{

**if**(chopstickArray.getLast(num).isAvailable())/\*如果左手边的筷子也可用,该哲学家状态设为在吃饭,其两侧的筷子都设置为不可用 \*/

{

chopstickArray.getnum(num).setAvailable(**false**);

chopstickArray.getLast(num).setAvailable(**false**);

String text = eatingTextArea.getText();

eatingTextArea.setText(text+**this** + " 在吃饭\\n");

**try**

{

Thread.*sleep*(10000);

}**catch**(Exception e)

{

e.printStackTrace();

}

}**else**

{ /\* 右手边的筷子可用,但是左手边的不可用\*/

String str = waitingTextArea.getText();

waitingTextArea.setText(str+**this**+" 等待左手边 "+chopstickArray.getLast(num)+"\\n");

**try**

{

wait(**new** Random().nextInt(100));

}**catch**(Exception e)

{

e.printStackTrace();

}

}

}**else**

{ /\* 如果哲学家右手边的筷子不可用则等待\*/

String str = waitingTextArea.getText();

waitingTextArea.setText(str+**this**+" 等待右手边 "+chopstickArray.getnum(num)+"\\n");

**try**

{

wait(**new** Random().nextInt(100));

}**catch**(Exception e)

{

e.printStackTrace();

}

}

}

state = **true**;

}

**public** **void** run()

{

**for**(**int** i = 0; i \< 10; ++i)

{

thinking();

eating();

}

}

**public** String toString()

{

**return** " 哲学家 " + num;

}

**private** **int** num;

**private** **boolean** state;

ChopstickArray chopstickArray;

TextArea thinkingTextArea;

TextArea eatingTextArea;

TextArea waitingTextArea;

}

**class** ChopstickArray /\* 存放筷子类的数组 \*/

{

**public** ChopstickArray(**int** size)

{

chopsticks = **new** Chopstick[size];

**for**(**int** i = 0; i \< chopsticks.length; ++i)

{

chopsticks[i] = **new** Chopstick(i);

}

}

**public** Chopstick getnum(**int** num)

{

**return** chopsticks[num];

}

**public** Chopstick getLast(**int** num)

{

**if**(num==0)

{

**return** chopsticks[chopsticks.length-1];

}**else**

{

**return** chopsticks[num-1];

}

}

**private** Chopstick[] chopsticks;

}

**class** Chopstick /\*筷子的类 \*/

{

**public** Chopstick(**int** num)

{

**this**.num = num;

}

**public** **boolean** isAvailable()

{

**return** available;

}

**public** **void** setAvailable(**boolean** available)

{

**this**.available = available;

}

**public** **int** getnum()

{

**return** num;

}

**public** **void** setnum(**int** num)

{

**this**.num = num;

}

**public** String toString()

{

**return** "筷子" + num;

}

**private** **volatile** **boolean** available = **true**; /\* 表示筷子是否可用 \*/

**private** **int** num;

}

  • 4
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
哲学家进餐问题是一个经典的并发问题,涉及多个哲学家在桌子上围坐并尝试拿起左右两侧的筷子进餐,但是只有当左右两侧的筷子都可用时,才能够进餐。如果所有哲学家都试图同时进餐,就会出现死锁的情况,因为每个哲学家都在等待其他人放下筷子。 以下是使用C++实现的一种解决方案,其中使用了互斥锁和条件变量来确保每个哲学家都能够进餐,而且不会出现死锁情况。 ```c++ #include <iostream> #include <thread> #include <mutex> #include <condition_variable> using namespace std; const int NUM_PHILOSOPHERS = 5; mutex forks[NUM_PHILOSOPHERS]; condition_variable cv[NUM_PHILOSOPHERS]; void philosopher(int id) { int left_fork = id; int right_fork = (id + 1) % NUM_PHILOSOPHERS; while (true) { // 拿起左边的叉子 unique_lock<mutex> left_lock(forks[left_fork]); // 尝试拿起右边的叉子 unique_lock<mutex> right_lock(forks[right_fork], defer_lock); // 如果右边的叉子被其他哲学家拿走了,就等待 while (!right_lock.try_lock()) { cv[left_fork].wait(left_lock); } // 吃饭 cout << "Philosopher " << id << " is eating." << endl; this_thread::sleep_for(chrono::seconds(1)); // 放下叉子 right_lock.unlock(); cv[(left_fork + NUM_PHILOSOPHERS - 1) % NUM_PHILOSOPHERS].notify_one(); left_lock.unlock(); cv[left_fork].notify_one(); // 思考 cout << "Philosopher " << id << " is thinking." << endl; this_thread::sleep_for(chrono::seconds(1)); } } int main() { thread philosophers[NUM_PHILOSOPHERS]; for (int i = 0; i < NUM_PHILOSOPHERS; i++) { philosophers[i] = thread(philosopher, i); } for (int i = 0; i < NUM_PHILOSOPHERS; i++) { philosophers[i].join(); } return 0; } ``` 在这个例子中,每个哲学家都是一个独立的线程,它们尝试拿起左右两侧的筷子,如果两侧的筷子都可用,就进餐一段时间,然后放下筷子,进行思考。当某个哲学家拿起了左边的筷子,但是右边的筷子被其他哲学家拿走时,它就会等待右边的筷子被放下,并通过条件变量通知左边的哲学家可以重新尝试拿起左边的筷子。这样,每个哲学家都可以进餐,并且不会出现死锁情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MIKE笔记(同名B站)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值