Java实验三 多线程

哲学家进餐问题

5个哲学家共用一张圆桌,分别坐在周围的5张椅子上,

在圆桌上有5个碗和5只筷子(注意是5只筷子,不是5双),

碗和筷子交替排列。他们的生活方式是交替地进行思考(thinking)和进餐(eating)。

平时,一个哲学家进行思考,饥饿时便试图取用其左右最靠近他的两只筷子,规定他必须先取左边的筷子,再取右边的筷子。

只有在他拿到两只筷子时才能进餐。

进餐完毕,放下筷子继续进行思考。

假如5位哲学家同时饥饿,各自拿起左边的筷子时,再去拿各自右边的筷子,因为无筷子可拿而陷入无期限等待(死锁)。

进餐完毕释放他用过的两只筷子,从而使更多的哲学家能够进餐。使用Java的多线程同步技术,实现上述解决方案。

解决方法一:破坏循环等待条件

最多允许4个哲学家同时去拿左边的筷子,即死锁四大必要条件之一——破坏循环等待条件。

synchronized关键字易混淆处:

用法:synchronized(一个对象){同步代码块}

这里synchronized锁住的不是这"一个对象",而是用这"一个对象"来当看门人,只允许一个线程来进入synchronized锁住的同步{代码块},所以这个代码块被称作“同步代码块”,当代码块执行完后,会让这“一个对象”(看门人),吼一声让其它线程进来。原理是:每一个java对象都关联了一个“ monitor lock”,当一个线程获取了 monitor lock 后,其它线程如果运行到获取同一个 monitor 的时候就会被 block 住。当这个线程执行完同步代码,则会释放 monitor lock。

下面代码,经过分析后能满足最多允许4个哲学家同时去拿左边的筷子的条件:

分析如下:

1.如果不加num<4,synchronized(left)后,如果另一个哲学家进程拿了right的筷子,另一个哲学家又拿了这个哲学家右边的筷子...依此类推,产生循环等待链,即产生死锁。就会产生死锁。

我们标记3个结点去分析各个代码块进入的最多线程数。

2.加了num<4这个条件后,可能会有5个线程,同时满足if判断语句(结点1);而又因为synchronized了一个筷子(left),所以最多有4个进程进入了synchronized(left){}代码块钟,执行了num++,(结点2),那么虽然此时可能有5个进程进入了if(num<4)语句,但是第5个进程也会被阻挡在synchronized外面。在synchronized(right)后,最多有2个进程去吃东西(结点3),因为只有5个筷子,同时只能让两个人去吃,其它3个人等待,看图比较容易明白。

代码如下:

class phiPerson implements Runnable{ //实现Runnable接口

	//全部使用static关键字 静态成员属于整个类,当系统第一次使用该类时,就会为其分配内存空间直到该类被卸载才会进行资源回收
//为什么用static修饰 因为多线程多个哲学家需要共享筷子这个对象
static Object[] chops;//static关键字修饰的变量 该类所有的对象共享同一个成员
static int Num = 0; //同时拿左手边筷子的人数 也是全局变量
private int pos; //当前哲学家的编号 私有变量 public phiPerson(int position,Object[] chops) { //构造函数
// TODO Auto-generated constructor stub
this.chops = chops;
this.pos = position;
} @Override
public void run() { //重写run方法
// TODO Auto-generated method stub
while(true) {
int right = (pos+1)%5; //我右边筷子在数组中的下标
int left = (pos)%5;//左边筷子在数组中的下标
if(Num < 4) { //最多允许4个人同时拿左手边的筷子
//结点1:5个进程都有可能进入这个地方
synchronized (chops[left]) { //锁 左手边的筷子 就是等待左边的人用我左手边的筷子吃完了后我再拿来吃。。
Num++;//同时拿左手边筷子的人的数量+1 //这里没有锁住num,有可能会使得在没有num++时,5个进程都进来,
//结点2:最多4个进程进到这个地方,因为synchronized (chops[left])后,需要left筷子的另1个进程要在外面等待
System.out.println(Num);
System.out.println("第"+(pos+1)+"个哲学家拿了左手边的筷子");
synchronized(chops[right]) {//右手边的筷子 锁 就是等待右手边的筷子没人拿了我再拿
//结点3:最多有2个进程进入到这个地方,因为只有5个筷子,所以最多两个人同时拿。
System.out.println("第"+(pos+1)+"个哲学家拿了右手边的筷子");
System.out.println("第"+(pos+1)+"个哲学家正在eating");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("第"+(pos+1)+"个哲学家吃完了,把筷子放回了原处,开始thinking");
Num--;//同时拿左手边筷子的人的数量-1
}
} }
}
}
} public class phiEat { public static void main(String[] args) {
// TODO Auto-generated method stub
Object [] chObject = new Object[5];
for(int i=0;i<5;i++) chObject[i] = i; //object必须要初始化一下呀 //5个哲学家
phiPerson firThread = new phiPerson(0,chObject);
phiPerson secThread = new phiPerson(1,chObject);
phiPerson thirThread = new phiPerson(2,chObject);
phiPerson fourThread = new phiPerson(3,chObject);
phiPerson fifThread = new phiPerson(4,chObject);
//开吃了
new Thread(firThread).start();
new Thread(secThread).start();
new Thread(thirThread).start();
new Thread(fourThread).start();
new Thread(fifThread).start();
} }

解决方法二:破坏请求和保持条件

方案:仅当一个哲学家左右两边的叉子都可用时才允许他抓起叉子,即破坏死锁四大条件之一——请求和保持条件

说明白点就是,不会出现某个哲学家拿一个筷子等一个筷子的情况,必须同时拿两个!

package philosopher;

public class ThreadTest {

	public static void main(String[] args) {
// TODO Auto-generated method stub
Chop fiveChops = new Chop();
new Philosopher(0,fiveChops).start();
new Philosopher(1,fiveChops).start();
new Philosopher(2,fiveChops).start();
new Philosopher(3,fiveChops).start();
new Philosopher(4,fiveChops).start();
} } class Philosopher extends Thread{
private int index;
private Chop chop;
public Philosopher(int index, Chop chop) {
// TODO Auto-generated constructor stub
this.index = index;
this.chop = chop;
} @Override
public void run() {
while(true) {
thinking();
chop.takeChop(index);
eating();
chop.putChop(index);
}
}
private void thinking(){
// TODO Auto-generated method stub
System.out.println("第"+index + "个哲学家正在思考...");
try {
sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} private void eating(){
// TODO Auto-generated method stub
System.out.println("第"+index + "个哲学家正在吃饭...");
try {
sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} } class Chop {
private Boolean[] chops = {false,false,false,false,false}; public synchronized void takeChop(int index) {
// TODO Auto-generated method stub
while(chops[index] || chops[(index+1)%5]) {
try {
wait(); //拿不到筷子就会被阻塞 进入等待池 从而不会再来竞争
} catch (Exception e) {
// TODO: handle exception
}
}
chops[index] = true;
chops[(index+1)%5] = true;
} public synchronized void putChop(int index) {
// TODO Auto-generated method stub
chops[index] = false;
chops[(index+1)%5] = false;
notifyAll();
} }

这里产生疑问点待解决,notifyAll和wait在此代码中如何工作。

参考文章:

[1] sunny_ss12 经典同步问题(二)---哲学家就餐问题

[2] Yun_Ge PV操作经典例题——哲学家进餐问题

[3] qiuhuilu JAVA多线程学习--哲学家就餐问题

Java哲学家进餐问题|多线程的更多相关文章

  1. Java 哲学家进餐

    某次操作系统实验存档.V 这个哲学家除了吃就知道睡.( ╯□╰ ) 哲学家.java: package operating.entity.philosophyeating; import operat ...

  2. Java哲学家进餐

    某次操作系统实验存档. 这个哲学家除了吃就是睡.. 哲学家.java: package operating.entity.philosophyeating; import operating.meth ...

  3. java笔记--超级类Object多线程的应用+哲学家进餐算法内部类与多线程结合

    关于Object类中的线程方法: Object类是所有Java类的 父类,在该类中定义了三个与线程操作有关的方法,使得所有的Java类在创建之后就支持多线程 这三个方法是:notify(),notif ...

  4. Java学习手记2——多线程

    一.线程的概念 CPU执行程序,就好比一个人在干事情一样,同一个时间你只能做一件事情,但是这样的效率实在是太低了,在你用电脑的时候,听歌就不能浏览网页,看电影就不能下载视频,你想想是不是很蛋疼. 所以 ...

  5. java 并发性和多线程 -- 读感 (一 线程的基本概念部分)

    1.目录略览      线程的基本概念:介绍线程的优点,代价,并发编程的模型.如何创建运行java 线程.      线程间通讯的机制:竞态条件与临界区,线程安全和共享资源与不可变性.java内存模型 ...

  6. Java 并发性和多线程

    一.介绍 在过去单 CPU 时代,单任务在一个时间点只能执行单一程序.之后发展到多任务阶段,计算机能在同一时间点并行执行多任务或多进程.虽然并不是真正意义上的“同一时间点”,而是多个任务或进程共享一个 ...

  7. Java 并发和多线程(一) Java并发性和多线程介绍[转]

    作者:Jakob Jenkov 译者:Simon-SZ  校对:方腾飞 http://tutorials.jenkov.com/java-concurrency/index.html 在过去单CPU时 ...

  8. Java核心知识点学习----多线程中的阻塞队列,ArrayBlockingQueue介绍

    1.什么是阻塞队列? 所谓队列,遵循的是先进先出原则(FIFO),阻塞队列,即是数据共享时,A在写数据时,B想读同一数据,那么就将发生阻塞了. 看一下线程的四种状态,首先是新创建一个线程,然后,通过s ...

  9. 利用Linux下的pthread_mutex_t类型来实现哲学家进餐问题

    首先说一下什么是哲学家进餐问题,这是操作系统课程中一个经典的同步问题, 问题如下:如上图,有6个哲学家和6根筷子(那个蓝色部分表示哲学家,那个紫色长条部分表示筷子),他们分别被编了0~5的号!如果某个 ...

随机推荐

  1. mysql优化:覆盖索引(延迟关联)

    前言 上周新系统改版上线,上线第二天就出现了较多的线上慢sql查询,紧接着dba 给出了定位及解决方案,这里较多的是使用延迟关联去优化. 而我对于这个延迟关联也是第一次听说(o(╥﹏╥)o),所以今天 ...

  2. Android五大布局详解——LinearLayout(线性布局)

    Android五大布局 本篇开始介绍Android的五大布局的知识,一个丰富的界面显示总是要有众多的控件来组成的,那么怎样才能让这些控件能够按你的想法进行摆放,从而自定义你所想要的用户界面呢?这就牵涉 ...

  3. Abp RabbitMqEventBus

    RabbitMQ安装介绍查看该网址 两个App都要配置 appsettings.json { "RabbitMQ": { "Connections": { &q ...

  4. 520表白酷炫html

    html表白神器,动态浮动爱心,话不多说,直接看截图吧! 下面附上完整代码.别忘了点个赞哦! <!doctype html> <html> <head> <m ...

  5. mysql 事务四要素杂谈

    事务四要素 对于数据库来说,并发性和准确性是数据库需要权衡的两个点. 类似于我们的应用系统,又要要性能还要要准确. 数据准确性这一条来说,最好的控制就是串行化,都别急,一个一个来.这样数据就没问题了. ...

  6. ReactNative: 使用Touchable触摸类组件

    一.简介 在应用程序中,最灵魂的功能就是交互.通过给应用程序的组件添加事件来实现交互,进而提高用户体验.然而,ReactNative并不能像Web开发那样可以给大多数的标签元素绑定click事件,例如 ...

  7. 基于V7的新版RL-USB V6.X + RL-FlashFS V6.X模板,操作CLASS10的SD卡速度12-15MB/S,含RTX5和FreeRTOS两版

    说明: 1.如果需要RL-USB源码的话,将DAPLink(CMSIS-DAP)里面的USB代码导出来即可,DAPLink开源了RL-USB的Device代码.      也可以反过来,在工程模板的基 ...

  8. 为什么老外不愿意用MyBatis?

    作者:陈龙 www.zhihu.com/question/309662829 Spring 团队的Josh Long自己在Twitter上做了一个调查.1625次投票,样本量不算大,但也能说明问题.和 ...

  9. 深入requests库params|data|json参数

    深入requests库params|data|json参数 一.params params:字典或者字节序列,作为参数增加到URL中.不仅访问URL,还可以向服务器携带参数. 简单来讲也就是说对于原来 ...

  10. 本地SQL Server数据库提示网络问题无法连接

    运行程序时发现本地SQLserver数据库无法连接,提示信息为:在与SQL Server 建立连接时出现与网络相关的或特定与实例的错误.未能找到或无法访问服务器.请验证实例名称是否正确并且SQL Se ...