哲学家就餐问题解决方案(java)

什么是哲学家就餐问题

哲学家就餐问题是计算机科学中的一个经典问题,用于演示在并行计算中多线程同步时产生的问题。题目要求是:五位哲学家围着一张桌子而坐,他们不交谈,每人面前放了一碗饭,每两个人之间放了一只筷子(总共五只筷子)。哲学家只能做两件事,吃饭和思考,吃饭的时候不思考,思考的时候不吃饭。哲学家只有拿起自己身边的两只筷子才能吃饭,拿筷子的顺序是先拿左手再拿右手(不同时)。

题目分析

因为哲学家之间不会交谈,故可能会发生一些很严重的情况:
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、程序运行结果截图
在这里插入图片描述
这样一来就可以实现两个哲学家同时吃饭,不会因为筷子等问题而导致冲突,最后所有哲学家全部吃完饭进入思考状态。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值