我们知道一个对象可以有synchronized方法或其他形式的加锁机制来防止别的线程在互斥还没释放的时候就访问这个对象。而且我们知道线程是会变成阻塞状态的(挂起),所以有时候就会发生死锁的情况:某个任务在等待另一个任务,而后者又在等待其它任务,这样一直下去,知道这个链条下的任务又在等待第一个任务释放锁,这样就形成了一个任务之间相互等待的连续循环,没有任务可以继续的情况。死锁的最大问题在于它发生的几率非常小,并不是我们一运行程序它就死锁了,而是会不知道那个时候程序就死锁并且我们很难重现当时出现死锁的情况。在这篇博客里我们只是从哲学家就餐问题的程序中感受下死锁现象,随便结合分析下出现死锁的几个条件,并不会讨论如何避免/解决死锁问题。

哲学家就餐问题是一个经典的关于死锁的问题,其大概的描述是:有五个哲学家,他们会花部分时间思考,花部分时间就餐。当他们思考的时候,他们不需要共享任何资源互不影响。而当他们就餐时,因为这里只有五只筷子,在他们每人都需要两只筷子的情况下,就会形成对筷子的竞争。这个问题并不是说百分百的会死锁,仅仅是存在这种可能而已。下面的代码就演示了这个问题,在注释里有对更多的细节的解释:

package IO;import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/*** 通过模拟哲学家问题来观察死锁的产生* 下面的代码是模拟的五个哲学家和五根筷子的经典哲学家问题* *///共享资源:筷子类
class Chopstick{private boolean token=false; //该筷子是否被使用了//使用筷子,如果该筷子已被别的线程使用,则当前线程调用wait()挂起public synchronized void take() throws InterruptedException{while(token){wait();}token=true;}//筷子使用完以后,放下筷子,并唤醒其它正在wait()的线程public synchronized void drop(){token=false;notifyAll();}
}class Philosopers implements Runnable{//左边和右边的筷子private Chopstick left;private Chopstick right;private final int id;//哲学家使用筷子的编号private final int pauseFactor; //暂停因子private Random rand = new Random(200);public Philosopers(Chopstick left,Chopstick right,int id,int pauseFactor){this.left=left;this.right=right;this.id=id;this.pauseFactor=pauseFactor;}//暂停随机时间private void pause() throws InterruptedException{ TimeUnit.MILLISECONDS.sleep(pauseFactor*rand.nextInt(100));}public void run(){try{while(!Thread.interrupted()){System.out.println(this+" "+"thinking........... ");//表示正在思考pause();//模拟思考时间System.out.println(this+" 取得左边的筷子");left.take(); //取得左边的筷子System.out.println(this+" 取得右边的筷子");right.take();System.out.println(this+"Eating...........");//就餐pause();//模拟就餐时间//放下筷子left.drop();right.drop();}}catch(InterruptedException ex){System.out.println(this+" 通过中断异常退出");}}public String toString(){return (id+1)+"号哲学家 ";}
}public class test {public static void main(String[] args) throws Exception{//可以通过调整pauseFactor从而调整哲学家思考的时间//思考时间越短则线程间对共享资源的竞争越强越容易产生死锁问题//pauseFacotr等于0的时候几乎每次都可以看到死锁问题的产生int pauseFactor=0;int size=5;Chopstick[] chopstick = new Chopstick[size];//五只筷子for(int i=0;i<size;i++){chopstick[i] = new Chopstick();}ExecutorService exec = Executors.newCachedThreadPool();//产生5个哲学家线程for(int i=0; i<size; i++){exec.execute(new Philosopers(chopstick[i],chopstick[(i+1)%size],i,pauseFactor));/*//下面的代码通过防止循环等待而阻止了死锁的产生//让前四位哲学家总是先拿左边的筷子,再拿右边的筷子,而让第五位哲学家先拿右边的筷子//这样可以打破循环等待的条件,实际上这时候第五位哲学家总是会由于取不到右边的筷子//而阻塞(它右边的筷子已经被第一位哲学家取了),所以也就不会去拿它左边的筷子,从而//第四位哲学家总是可以取得两只筷子第一个就餐,从而不会产生循环等待的结果(从输出中可以看到这一点).if(i<(size-1)){exec.execute(new Philosopers(chopstick[i],chopstick[(i+1)%size],i,pauseFactor));}else{exec.execute(new Philosopers(chopstick[(i+1)%size],chopstick[i],i,pauseFactor));}*/}exec.shutdown();System.out.println("press 'Enter' to quit");System.in.read();exec.shutdownNow();}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120

在上面的程序中我们可以设定了哲学家的思考时间和就餐时间都为一个随机时间,并且我们可以通过pauseFactor来对其进行调节,多次设定这个值然后运行程序就会发现当pauseFactor的值较小的时候发生死锁的概率就比较大,当pauseFactor为0即哲学家不花时间进行思考而总是去抢筷子的时候,几乎每次都会发生死锁。这就说明对资源的竞争越激烈就越容易发生死锁。

学过操作系统的都知道要发生死锁有四个条件需要满足,下面我们就结合上面的问题来分析下这四个条件: 
1).互斥。线程使用的资源中至少有一个是不能共享的。这里,一根Chopstick一次就只能被一个Philosopher使用 
2).至少有一个任务它必须持有一个资源且正在等待获取一个当前被别的任务持有的资源。也就是说,要发生死锁,Philosopher必须持有一个Chopstick并正在等待另一根。 
3).资源不能被任务抢占,任务必须把释放资源当成普通事件。在上面中Philosopher不会去抢其它Philosopher手中的Chopstick。 
4).必须有循环等待,这时候一个任务等待其它任务所持有的资源,后者又在等待另一个任务持有的资源,这样一直下去,直到一个任务在等待第一个任务所持有的资源。在上面的代码中每个Philosopher都会试图得到左边的Chopstick然后得到右边的Chopstick,这样就产生了循环等待。

死锁出现必须要满足上面四个条件,所以避免死锁的方法就是破坏四个条件中的一个。上面有一段注释掉的代码通过调整最后一位哲学家获取Chopstick的顺序(与其它哲学家相反)而达到了破坏第四个循环等待条件的效果,从而防止了死锁的产生。其它的防止死锁的方法就不在这里讨论了。Java中并没有从语言层面上有防止死锁的措施,所以要防止死锁的产生,只有靠我们自己的小心谨慎了。

Thinking in Java---从哲学家就餐问题看死锁现象相关推荐

  1. 哲学家就餐(避免死锁)(多进程版)

    哲学家就餐(避免死锁)(多进程版) 哲学家就餐利用信号量在多进程之间实现 下面展示一些代码片段 #include <stdio.h> #include <unistd.h> # ...

  2. 哲学家就餐问题python_哲学家就餐问题与死锁

    问题描述 哲学家就餐问题(Dining philosophers problem)是在计算机科学中的一个经典问题,用来演示在并发计算中多线程同步(Synchronization)时产生的问题. 哲学家 ...

  3. java中哲学家就餐死锁_哲学家就餐问题与死锁总结

    死锁的四个条件: (1) 互斥条件:一个资源每次只能被一个进程使用. (2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放. (3) 不剥夺条件:进程已获得的资源,在末使用完之前 ...

  4. 哲学家就餐问题实验报告

    哲学家就餐问题 两个地方应该是pv操作,pv都是操作元语,不可中断 p操作是将信号量-1 v操作是将信号量+1 pv一定要配对使用 哲学家进餐可以通过信号量机制解决,避免死锁 注释如下: test(i ...

  5. Java基础学习之并发篇:哲学家就餐问题

    学习目标 哲学家就餐问题是在计算机科学中的一个经典问题,用来演示在并行计算中多线程同步时产生的问题.在1971年,著名的计算机科学家艾兹格·迪科斯彻提出了一个同步问题,即假设有五台计算机都试图访问五份 ...

  6. Java多线程学习四十二:有哪些解决死锁问题的策略和哲学家就餐问题

    线上发生死锁应该怎么办 如果线上环境发生了死锁,那么其实不良后果就已经造成了,修复死锁的最好时机在于"防患于未然",而不是事后补救.就好比发生火灾时,一旦着了大火,想要不造成损失去 ...

  7. 多线程之哲学家就餐问题(java代码含注释)

    什么是哲学家就餐问题 有五个哲学家在一张桌上,他们交替思考和吃饭.每个人只能拿自己左右手边的叉子,当他们拿到两只叉子的时候才能吃饭,吃完饭就放下叉子开始思考,每个哲学家不能同时拿起两只叉子.用程序实现 ...

  8. php 哲学家进餐,IPC问题-哲学家就餐(示例代码)

    如上图,有五位哲学家,盘中的食物只有左右两个叉子都拿起才能吃.哲学家在桌上只有思考(等待)和吃面(执行).看起来最多是只有2个人能同时吃. 版本一:这个思路的最糟糕的就是都拿起左边叉子,那样都没法吃了 ...

  9. 哲学家就餐与死锁问题,死锁产生的条件以及解决方案

    请结合经典案例-哲学家就餐,来谈谈你对死锁的理解,以及怎么预防和解除死锁? 哲学家就餐 描述:在一张圆桌上,有n个哲学家,n支筷子,他们的生活方式只是交替地进行思考和进餐,饥饿时便试图取其左.右最靠近 ...

最新文章

  1. 微信小程序获取用户设备的信息
  2. 在腾讯,如何做 Code Review?
  3. Android开发神器:OkHttp框架源码解析
  4. 全局内存BSS,DATA,RODATA的区别以及其他内存区间相关
  5. Mybatis 实现SQL拦截并在控制台打印SQL和参数
  6. SpringBoot Bean配置
  7. 第七十六期:糟糕!服务器被植入挖矿木马,CPU飙升200%
  8. 经典面试题(52):以下代码将输出的结果是什么?
  9. python实现语义分割_如何用PyTorch进行语义分割?一文搞定
  10. python的urllib2包基本使用方法
  11. 内部曝料——博文年会之《武林外传》
  12. 配置php apache,apache如何配置php
  13. 任泽平:中国自动驾驶发展报告2020(上)
  14. php和python的选择排序算法,基于python的七种经典排序算法的详细介绍
  15. php 按钮美化,input(file)按钮样式美化第二种
  16. (4)bootstrap标签页
  17. JAXWS CXF HelloWorld + MyEclipse + Maven + Jetty Byron自學視頻01
  18. Python PyCharm Django 搭建web开发环境
  19. IDEA中插件加载不出来问题解决
  20. 使用realsense D435i实现机械臂对物体的自动抓取总结

热门文章

  1. 链表实现c语言通讯录管理系统,C++链表实现通讯录管理系统
  2. python 将base64字符串还原成图片保存
  3. mysql 1593_Linux高可用(HA)之MySQL主从复制中出现1593错误码的低级错误
  4. HTML学生个人网站作业设计:电影网站设计——叮当电影(5页) HTML+CSS+JavaScript 简单DIV布局个人介绍网页模板代码 DW学生个人网站制作成品下载
  5. 服务器和交换机物理连接_Brocade博科交换机 SAN存储区域网络
  6. Flink on Yarn(HA配置)
  7. day fit into much one too_2018年广东省高考英语听说考试真题A-E(附答案)
  8. 处理深度学习中数据集不平衡问题方法汇总
  9. SQL回炉重造07_函数
  10. Word字数统计怎么用?2003/2007/2010统计字数全攻略!