什么是哲学家就餐问题
哲学家就餐问题是计算机科学中的一个经典问题,用于演示在并行计算中多线程同步时产生的问题。题目要求是:五位哲学家围着一张桌子而坐,他们不交谈,每人面前放了一碗饭,每两个人之间放了一只筷子(总共五只筷子)。哲学家只能做两件事,吃饭和思考,吃饭的时候不思考,思考的时候不吃饭。哲学家只有拿起自己身边的两只筷子才能吃饭,拿筷子的顺序是先拿左手再拿右手(不同时)。
题目分析
因为哲学家之间不会交谈,故可能会发生一些很严重的情况:
1、发生死锁,即每个哲学家都左手拿着筷子,等待着右侧的筷子空闲,这就发生了死锁,谁也吃不了饭。
2、出现他们同时拿起左手的筷子,又同时放下左手的筷子,循环往复。
3、不加锁则会出现五个人同时吃饭的情况。
解题思路
这是一个多线程的同步问题,如果简单地让五个线程去跑,则会出现五个线程对资源的同时使用(五个人同时吃饭)的情况,我们要为其方法加锁。那么什么是锁呢?每个对象都有一把唯一的锁,假设我们这里用到的是通过Object类创建的obj对象,通过synchronize(obj){ //加锁的内容 } 方法我们可以使得被修饰的代码块获得对象锁,从而获得竞争性资源的使用权,执行完代码后会自动让出对象锁,其他线程便有机会获得此锁,得到竞争性资源的使用权。这样你们是否觉得会出现哲学家轮流一个一个吃饭的问题,因为我们有五只筷子,最多可以有两个人同时吃饭,如果只有一个人吃就会导致资源的浪费。我的解决思路是仅仅让synchronize修饰取筷子的这个过程涉及到的代码块,这样当一个哲学家刚开始吃饭的时候,可以进行另一个哲学家的取筷子判断,这样就可以有两个哲学家同时吃饭了。
程序的流程是:判断左右手是否同时有空闲的筷子,如果没有,就持续思考,如果有,则先取左手筷子再取右手筷子,然后停止思考并吃饭,吃完饭后清洗筷子放到桌面并继续思考,此时处于吃饱状态,不会再重复吃饭。
1、测试类
public class Test {
public static void main(String[] args){
Object obj= new Object(); //锁
boolean [] chopsticks ={false,false,false,false,false}; //筷子
for(int i=0;i<5;i++)
{
Philosophy phi = new Philosophy(i, false, false, true, true); //创建线程
phi.setobj(obj); //传obj对象
phi.setchopsticks(chopsticks); //传筷子对象
phi.start(); //线程启动
}
}
}
2、线程类
public class Philosophy extends Thread{
boolean [] chopsticks; //true:拿起状态 false:放下状态
private int i;
private Boolean leftFlag=false; //false:未拿筷子 true:拿起筷子
private Boolean rightFlag=false;
private Boolean hungry=true; //true:未吃饭,饥饿 false:已经吃完了饭
private Boolean thinkFlag=false; //false:没有思考 true:在思考
private Object obj;
public Philosophy(int i , Boolean leftFlag , Boolean rightFlag , Boolean hungry ,Boolean thinkFlag) //构造方法
{
this.i=i;
this.leftFlag=leftFlag;
this.rightFlag=rightFlag;
this.hungry = hungry;
this.thinkFlag=thinkFlag;
}
public void run(){
while(true){ //循环执行判断
this.eat();
this.think();
}
}
public void eat(){
synchronized (obj)
{
if(!chopsticks[this.i]&&!chopsticks[(this.i+1)%5]&&!this.leftFlag&&!this.rightFlag&&this.hungry) //可以拿筷子
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
chopsticks[(this.i+1)%5]=true; //表示筷子被拿起
this.leftFlag=true; //左手有筷子
System.out.println("哲学家"+this.i+"拿起了左边的筷子");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
chopsticks[this.i]=true;
this.rightFlag=true;
System.out.println("哲学家"+this.i+"拿起了右边的筷子");
}
}
if(this.leftFlag&&this.rightFlag&&this.hungry) //满足条件,开始吃饭
{
System.out.println("哲学家"+this.i+"开始吃饭");
this.thinkFlag=false; //停止思考
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("哲学家"+this.i+"吃完了饭");
this.hungry =false; //不饿了
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("哲学家"+this.i+"擦净并放下了筷子");
System.out.println("哲学家"+this.i+"开始思考");
this.thinkFlag=true; //思考标志为true
this.leftFlag=false; //放下筷子,手上无筷子
this.rightFlag=false;
chopsticks[i]=false; //筷子被放下
chopsticks[(i+1)%5]=false;
}
}
public void think(){
if(!this.leftFlag&&!this.rightFlag&&!this.thinkFlag)
{
thinkFlag=true; //思考标志置为true
System.out.println("哲学家"+i+"开始思考");
}
}
public void setobj(Object obj) {
// TODO Auto-generated method stub
this.obj=obj;
}
public void setchopsticks(boolean[] chopsticks) {
// TODO Auto-generated method stub
this.chopsticks=chopsticks;
}
}
3、程序运行结果截图
这样一来就可以实现两个哲学家同时吃饭,不会因为筷子等问题而导致冲突,最后所有哲学家全部吃完饭进入思考状态。