零、哲学家进餐问题


  哲学家就餐问题可以这样表述,假设有五位哲学家围坐在一张圆形餐桌旁,做以下两件事情之一:吃饭,或者思考。吃东西的时候,他们就停止思考,思考的时候也停止吃东西。餐桌中间有一大碗意大利面,每两个哲学家之间有一只餐叉。因为用一只餐叉很难吃到意大利面,所以假设哲学家必须用两只餐叉吃东西。他们只能使用自己左右手边的那两只餐叉。哲学家就餐问题有时也用米饭和筷子而不是意大利面和餐叉来描述,因为很明显,吃米饭必须用两根筷子。

  哲学家从来不交谈,这就很危险,可能产生死锁,每个哲学家都拿着左手的餐叉,永远都在等右边的餐叉(或者相反)。即使没有死锁,也有可能发生资源耗尽。例如,假设规定当哲学家等待另一只餐叉超过五分钟后就放下自己手里的那一只餐叉,并且再等五分钟后进行下一次尝试。这个策略消除了死锁(系统总会进入到下一个状态),但仍然有可能发生“活锁”。如果五位哲学家在完全相同的时刻进入餐厅,并同时拿起左边的餐叉,那么这些哲学家就会等待五分钟,同时放下手中的餐叉,再等五分钟,又同时拿起这些餐叉。

  首先我们先来看这样一串代码及其运行结果:

import java.util.concurrent.Semaphore;public class MealOfPhilosopher {static final Semaphore[] chopsticks = new Semaphore[5];static {for (int i = 0; i < 5; i++) {chopsticks[i] = new Semaphore(1);}}public static void main(String[] args) {for (int i = 0; i < 5; i++) {int j = i;new Thread(()->{while (true) {try {chopsticks[j].acquire();System.out.println(Thread.currentThread().getName() + "拿走了他左边的" + j + "号筷子");chopsticks[(j + 1) % 5].acquire();System.out.println(Thread.currentThread().getName() + "拿走了他右边的" + (j + 1) % 5 + "号筷子");System.out.println(Thread.currentThread().getName() + "正在吃饭。");chopsticks[j].release();System.out.println(Thread.currentThread().getName() + "放下了他左边的" + j + "号筷子");chopsticks[(j + 1) % 5].release();System.out.println(Thread.currentThread().getName() + "放下了他右边的" + (j + 1) % 5 + "号筷子");System.out.println(Thread.currentThread().getName() + "正在思考。");} catch (InterruptedException e) {e.printStackTrace();}}}, "哲学家" + i + "号").start();}}}



  很明显,死锁住了。我们要必然要避免死锁,如何避免呢?

  那我们就要分析死锁是如何产生的。

  死锁产生的四个条件分别是:① 互斥条件、② 不剥夺条件、③ 请求和保持条件、④ 循环等待条件,我们只要破坏掉任何一个条件就可以避免死锁了。

一、破坏互斥条件

  很遗憾的是,这种策略并不能应用于哲学家进餐问题。并不是像内存一样同一时刻可以有多个进程访问,同一个筷子同一时刻只能让一个哲学家使用,也就是互斥访问,所以破坏互斥条件无法应用于哲学家进餐问题。

  spooling技术:各个需要使用打印机的进程把需要输出的内容交给输出进程,输出进程负责调用打印机,其他进程无需进入阻塞状态。把互斥资源改造成了共享资源。

二、破坏不剥夺条件


  java API中的Semaphore实现类的无参acquire()方法是无法获取资源就一直等待直到有资源,而他的有参函数tryAcquire(long timeout, TimeUnit unit)则是尝试获取一份资源,若timeout个时间单位内都没有获得,则放弃手中资源并返回false,TimeUnit是时间单位的枚举类型。

  解决方案:

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;public class MealOfPhilosopher {static final Semaphore[] chopsticks = new Semaphore[5];static final Semaphore mutex = new Semaphore(1);static {for (int i = 0; i < 5; i++) {chopsticks[i] = new Semaphore(1);}}public static void main(String[] args) {for (int i = 0; i < 5; i++) {int j = i;new Thread(()->{while (true) {try {System.out.println(Thread.currentThread().getName() + "拿走了他左边的" + j + "号筷子");if (!chopsticks[j].tryAcquire(10, TimeUnit.SECONDS))System.out.println(Thread.currentThread().getName() + "等待了好长时间,他只好放下他左边的" + j + "号筷子");System.out.println(Thread.currentThread().getName() + "拿走了他右边的" + (j + 1) % 5 + "号筷子");if (!chopsticks[(j + 1) % 5].tryAcquire(10, TimeUnit.SECONDS))System.out.println(Thread.currentThread().getName() + "等待了好长时间,他只好放下他右边的" + (j + 1) % 5 + "号筷子");System.out.println(Thread.currentThread().getName() + "正在吃饭。");System.out.println(Thread.currentThread().getName() + "放下了他左边的" + j + "号筷子");chopsticks[j].release();System.out.println(Thread.currentThread().getName() + "放下了他右边的" + (j + 1) % 5 + "号筷子");chopsticks[(j + 1) % 5].release();System.out.println(Thread.currentThread().getName() + "正在思考。");} catch (InterruptedException e) {e.printStackTrace();}}}, "哲学家" + i + "号").start();}}}

三、破坏请求和保持条件


  而在哲学家进餐问题中我们可以通过一个互斥锁mutex或者java语言的synchronized块来进行静态分配

import java.util.concurrent.Semaphore;public class MealOfPhilosopher {static Semaphore[] chopsticks = new Semaphore[5];static Semaphore mutex = new Semaphore(1);static {for (int i = 0; i < 5; i++) {chopsticks[i] = new Semaphore(1);}}public static void main(String[] args) {for (int i = 0; i < 5; i++) {int j = i;new Thread(()->{while (true) {try {mutex.acquire();chopsticks[j].acquire();System.out.println(Thread.currentThread().getName() + "拿走了他左边的" + j + "号筷子");chopsticks[(j + 1) % 5].acquire();System.out.println(Thread.currentThread().getName() + "拿走了他右边的" + (j + 1) % 5 + "号筷子");mutex.release();System.out.println(Thread.currentThread().getName() + "正在吃饭。");chopsticks[j].release();System.out.println(Thread.currentThread().getName() + "放下了他左边的" + j + "号筷子");chopsticks[(j + 1) % 5].release();System.out.println(Thread.currentThread().getName() + "放下了他右边的" + (j + 1) % 5 + "号筷子");System.out.println(Thread.currentThread().getName() + "正在思考。");} catch (InterruptedException e) {e.printStackTrace();}}}, "哲学家" + i + "号").start();}}}
import java.util.concurrent.Semaphore;public class MealOfPhilosopher {static final Semaphore[] chopsticks = new Semaphore[5];static {for (int i = 0; i < 5; i++) {chopsticks[i] = new Semaphore(1);}}public static void main(String[] args) {for (int i = 0; i < 5; i++) {int j = i;new Thread(()->{while (true) {try {synchronized(chopsticks) {chopsticks[j].acquire();System.out.println(Thread.currentThread().getName() + "拿走了他左边的" + j + "号筷子");chopsticks[(j + 1) % 5].acquire();System.out.println(Thread.currentThread().getName() + "拿走了他右边的" + (j + 1) % 5 + "号筷子");}System.out.println(Thread.currentThread().getName() + "正在吃饭。");chopsticks[j].release();System.out.println(Thread.currentThread().getName() + "放下了他左边的" + j + "号筷子");chopsticks[(j + 1) % 5].release();System.out.println(Thread.currentThread().getName() + "放下了他右边的" + (j + 1) % 5 + "号筷子");System.out.println(Thread.currentThread().getName() + "正在思考。");} catch (InterruptedException e) {e.printStackTrace();}}}, "哲学家" + i + "号").start();}}}

四、破坏循环等待条件


  顺序资源分配法如果某进程一次申请一号资源、二号资源,然而他先试用二号资源,在使用一号资源,就会使得一号资源长期空闲。

  应用到代码里面他具体就是,给筷子分成0~4号,0号哲学家先拿左手0后拿右手1,其他都是先拿先左后右,只有四号左手是4,右手是0,所以必须先拿右再拿左。

import java.util.concurrent.Semaphore;public class MealOfPhilosopher {static final Semaphore[] chopsticks = new Semaphore[5];static {for (int i = 0; i < 5; i++) {chopsticks[i] = new Semaphore(1);}}public static void main(String[] args) {for (int i = 0; i < 5; i++) {int j = i;if (j < 4) {new Thread(() -> {while (true) {try {chopsticks[j].acquire();System.out.println(Thread.currentThread().getName() + "拿走了他左边的" + j + "号筷子");chopsticks[j + 1].acquire();System.out.println(Thread.currentThread().getName() + "拿走了他右边的" + (j + 1) + "号筷子");System.out.println(Thread.currentThread().getName() + "正在吃饭。");chopsticks[j].release();System.out.println(Thread.currentThread().getName() + "放下了他左边的" + j + "号筷子");chopsticks[j + 1].release();System.out.println(Thread.currentThread().getName() + "放下了他右边的" + (j + 1) + "号筷子");System.out.println(Thread.currentThread().getName() + "正在思考。");} catch (InterruptedException e) {e.printStackTrace();}}}, "哲学家" + i + "号").start();} else {new Thread(() -> {while (true) {try {chopsticks[0].acquire();System.out.println(Thread.currentThread().getName() + "拿走了他右边的0号筷子");chopsticks[4].acquire();System.out.println(Thread.currentThread().getName() + "拿走了他左边的4号筷子");System.out.println(Thread.currentThread().getName() + "正在吃饭。");chopsticks[0].release();System.out.println(Thread.currentThread().getName() + "放下了他右边的0号筷子");chopsticks[4].release();System.out.println(Thread.currentThread().getName() + "放下了他左边的4号筷子");System.out.println(Thread.currentThread().getName() + "正在思考。");} catch (InterruptedException e) {e.printStackTrace();}}}, "哲学家" + i + "号").start();}}}}

【操作系统】结合哲学家进餐问题分析如何预防死锁相关推荐

  1. 【操作系统】哲学家进餐问题

    问题描述 一张圆桌上坐着5名哲学家,每两个哲学家之间的桌上摆着一根筷子,桌子的中间是一碗米饭,哲学家们倾注毕生精力用于思考和进餐,哲学家在思考时,并不影响他人.只有当哲学家饥饿时,才试图拿起左.右两根 ...

  2. 操作系统:哲学家进餐问题

    哲学家进餐问题 五个哲学家围着一张圆桌,每个哲学家面前放着食物.哲学家的生活有两种交替活动:吃饭以及思考.当一个哲学家吃饭时,需要先拿起自己左右两边的两根筷子,并且一次只能拿起一根筷子. 下面是一种错 ...

  3. 【操作系统】“哲学家进餐”问题

    "哲学家进餐"问题 有五个哲学家,他们的生活方式是交替地进行思考和进餐.他们共用一张圆桌,分别坐在五张椅子上. 在圆桌上有五个碗和五支筷子,平时一个哲学家进行思考,饥饿时便试图取用 ...

  4. 哲学家进餐问题(java模拟死锁及解决方案)

    一.问题描述 哲学家进餐问题是由 Dijkstra 提出并解决的,该问题是描述有五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五只筷子,他们的生活方式是交替地进行思考和进餐.平时 ...

  5. 操作系统之进程管理:18、预防死锁

    17.预防死锁 思维导图 1.破坏互斥条件 2.破坏不剥夺条件 3.破坏请求和保持条件 4.破坏循环等待条件 思维导图 1.破坏互斥条件 2.破坏不剥夺条件 3.破坏请求和保持条件 4.破坏循环等待条 ...

  6. 哲学家进餐问题三种解决死锁问题

    方案一 至多允许有四位哲学家同时去拿左边的筷子,然后在允许拿右边的筷子,最终能保证至少有一位哲学家能够进餐,并在用毕时能同时释放他用过的两只筷子,从而使更多的哲学家能够进餐 semaphore cho ...

  7. 操作系统(四) | 经典进程的同步问题(生产者--消费者问题、哲学家进餐问题、读者--写者问题)

    文章目录 生产者--消费者问题 分析 实现 哲学家进餐问题 方法一:最多4人同时拿左筷子,最终保证一人能进餐 方法二:同时给左右筷子 解法1:AND信号量 解法2:信号量保护机制 方法三:让奇数先左后 ...

  8. 哲学家就餐问题--信号量和互斥量预防死锁

    哲学家就餐问题可以采取预防死锁的方案,就是使用互斥量和信号量锁定资源. 互斥量: 对资源进行锁定的意思就是说,当一个哲学家使用叉子的时候,他首先要先把叉子锁定,然后,拿起来.这个时候如果别的哲学家也来 ...

  9. 【操作系统】-- PV原语(哲学家进餐问题)

    微信搜索:编程笔记本 微信搜索:编程笔记本 微信搜索:编程笔记本 点击上方蓝字关注我,我们一起学编程 欢迎小伙伴们分享.转载.私信.赞赏 小伙伴儿们看完以后可不可以帮我点亮一下在看呀~ 信号量与进程同 ...

  10. 2.7操作系统(读者—写者问题 哲学家进餐问题 管程 )

    目录 1.读者-写者问题 2.哲学家进餐问题 实现 3.管程 1.为什么要引入管程? ​2.管程的定义和基本特征 3.扩展1:用管程解决生产者消费者问题 4.扩展2:Java中类似于管程的机制  个人 ...

最新文章

  1. iOS--资源--优秀app发掘
  2. 机器视觉-EasyDL商品检测-标准版-Demo
  3. 向人类进化史看齐,编程语言的“别样”编年史
  4. 用Python做垃圾分类
  5. python技术介绍_Python编程语言基础技术框架()之函数介绍
  6. VC窗口形状的绘制---SetWindowRgn
  7. POJ 2808 校门外的树
  8. 近 5 亿人的隐私,毁于一位程序员之手?
  9. JavaScript中的正则表达式详解
  10. 虚拟机中安装win7
  11. php 入库乱码,php 中文字符入库或显示乱码问题的解决方法_PHP教程
  12. wamp phpcms部署网站问题
  13. 宅家36天咸鱼翻身入职腾讯,值得收藏!
  14. java调用geckofx_Scala学习(三)数组相关操作
  15. matlab之统计和机器学习工具箱
  16. 苹果cms替换资源_苹果maccms v10二次更新只替换某一个资源图片
  17. Json Schema 是什么?
  18. unity游戏模型获取- AssertStudio(原GuiStudio)(以第二银河为例)
  19. 储存卡格式化怎么恢复?给力的操作!
  20. 群内2018_4月讨论整理2

热门文章

  1. 学计算机装机,学习电脑装机全过程 组装台式机图解教程(12)
  2. SpringBoot图片上传报错:The field headImg exceeds its maximum permitted size of 1048576 bytes.
  3. 智能android电视更换播放器,智能电视最强播放器—KODI常见使用技巧全方位解读...
  4. python日期相减得到年龄_在dataframe两列日期相减并且得到具体的月数实例
  5. TypeError: Cannot set property ‘styles‘ of undefined
  6. 大小限制_微信传文件有大小限制?PPT太大怎么传?PPT自带三大压缩功能
  7. 各个地图经纬度转换工具类
  8. R语言——查看内置数据集
  9. 面试必备 | 带你彻底搞懂 Python 生成器。
  10. C#重写和重载的区别分析