哲學家進餐問題


參考:https://blog.csdn.net/fuziwang/article/details/79809994

1.問題描述:

哲學家進餐問題描述有五個哲學家,他們的生活方式是交替地進行思考和進餐,哲學家們共用一張圓桌,分別坐在周圍的五張椅子上,在圓桌上有五個碗和五支筷子,平時哲學家進行思考,飢餓時便試圖取其左、右最靠近他的筷子,只有在他拿到兩支筷子時才能進餐,該哲學家進餐完畢后,放下左右兩只筷子又繼續思考。

約束條件

(1)只有拿到兩只筷子時,哲學家才能吃飯。

(2)如果筷子已被別人拿走,則必須等別人吃完之后才能拿到筷子。

(3)任一哲學家在自己未拿到兩只筷子吃完飯前,不會放下手中已經拿到的筷子。

2.求解方法

(1).信號量的設置

放在桌子上的筷子是臨界資源,在一段時間內只允許一位哲學家使用,為了實現對筷子的互斥訪問,可以用一個信號量表示筷子,由這五個信號量構成信號量數組

semaphore chopstick[5] = {1,1,1,1,1};
while(true)
{
	/*當哲學家飢餓時,總是先拿左邊的筷子,再拿右邊的筷子*/
	wait(chopstick[i]);
	wait(chopstick[(i+1)%5]);
 
	// 吃飯
 
	/*當哲學家進餐完成后,總是先放下左邊的筷子,再放下右邊的筷子*/
	signal(chopstick[i]);
	signal(chopstick[(i+1)%5]);
}

上述的代碼可以保證不會有兩個相鄰的哲學家同時進餐,但卻可能引起死鎖的情況。

假如五位哲學家同時飢餓而都拿起的左邊的筷子,就會使五個信號量chopstick都為0,當他們試圖去拿右手邊的筷子時,都將無筷子而陷入無限期的等待。

(2)避免死鎖

策略一

原理:至多只允許四個哲學家同時進餐,以保證至少有一個哲學家能夠進餐,最終總會釋放出他所使用過的兩支筷子,從而可使更多的哲學家進餐。定義信號量count,只允許4個哲學家同時進餐,這樣就能保證至少有一個哲學家可以就餐。

semaphore chopstick[5]={1,1,1,1,1};
semaphore count=4; // 設置一個count,最多有四個哲學家可以進來
void philosopher(int i)
{
	while(true)
	{
		think();
		wait(count); //請求進入房間進餐 當count為0時 不能允許哲學家再進來了
		wait(chopstick[i]); //請求左手邊的筷子
		wait(chopstick[(i+1)%5]); //請求右手邊的筷子
		eat();
		signal(chopstick[i]); //釋放左手邊的筷子
		signal(chopstick[(i+1)%5]); //釋放右手邊的筷子
		signal(count); //退出房間釋放信號量
	}
}
策略二

原理:僅當哲學家的左右兩支筷子都可用時,才允許他拿起筷子進餐。可以利用AND 型信號量機制實現,也可以利用信號量的保護機制實現。利用信號量的保護機制實現的思想是通過記錄型信號量mutex對取左側和右側筷子的操作進行保護,使之成為一個原子操作,這樣可以防止死鎖的出現。描述如下:

semaphore chopstick[5]={1,1,1,1,1};
void philosopher(int i)
{
	while(true)
	{
		/* 這個過程中可能只能由一個人在吃飯 */
		think();
		wait(mutex); // 保護信號量
		wait(chopstick[(i+1)%5]); // 請求右手邊的筷子
		wait(chopstick[i]); // 請求左手邊的筷子
		signal(mutex); // 釋放保護信號量
		eat();
		signal(chopstick[(i+1)%5]); // 釋放右手邊的筷子
		signal(chopstick[i]); // 釋放左手邊的筷子
	}
} 
策略三

原理:規定奇數號的哲學家先拿起他左邊的筷子,然后再去拿他右邊的筷子;而偶數號的哲學家則先拿起他右邊的筷子,然后再去拿他左邊的筷子。按此規定,將是1、2號哲學家競爭1號筷子,3、4號哲學家競爭3號筷子。即五個哲學家都競爭奇數號筷子,獲得后,再去競爭偶數號筷子,最后總會有一個哲學家能獲得兩支筷子而進餐。

semaphore chopstick[5]={1,1,1,1,1};
void philosopher(int i)
{
	while(true)
	{
		think();
		if(i%2 == 0) //偶數哲學家,先右后左。
		{
			wait (chopstick[(i + 1)%5]) ;
			wait (chopstick[i]) ;
			eat();
			signal (chopstick[(i + 1)%5]) ;
			signal (chopstick[i]) ;
		}
		else //奇數哲學家,先左后右。
		{
			wait (chopstick[i]) ;
			wait (chopstick[(i + 1)%5]) ;
			eat();
			signal (chopstick[i]) ;
			signal (chopstick[(i + 1)%5]) ;
		}
	}

3.代碼實現

用java代碼實現第二種方法:

public class ThreadTest{
    public static void main(String[] args) {
        Fork fork = new Fork();
        new Philosopher("0", fork).start();
        new Philosopher("1", fork).start();
        new Philosopher("2", fork).start();
        new Philosopher("3", fork).start();
        new Philosopher("4", fork).start();
    }

}
class Philosopher extends Thread{
    private String name;
    private Fork fork;

    public Philosopher(String name, Fork fork) {
        super(name);
        this.name = name;
        this.fork = fork;
    }

    @Override
    public void run() {
        while (true){
            thinking();
            fork.takeFork();
            eating();
            fork.putFork();
        }

    }

    public void eating(){
        System.out.println("I am Eating:"+name);
        try {
            sleep(1000);//模擬吃飯,占用一段時間資源
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


    public void thinking(){
        System.out.println("I am Thinking:"+name);
        try {
            sleep(1000);//模擬思考
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


    public Fork getFork() {
        return fork;
    }

    public void setFork(Fork fork) {
        this.fork = fork;
    }
}
class Fork{
    /*5只筷子,初始為都未被用*/
    private boolean[] used={false,false,false,false,false,false};
    public synchronized void takeFork(){
        String name = Thread.currentThread().getName();
        int i = Integer.parseInt(name);
        while(used[i]||used[(i+1)%5]){
            try {
                wait();//如果左右手有一只正被使用,等待
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        used[i ]= true;
        used[(i+1)%5]=true;
    }

    public synchronized void putFork(){
        String name = Thread.currentThread().getName();
        int i = Integer.parseInt(name);
        used[i]= false;
        used[(i+1)%5]=false;
        notifyAll();//喚醒其他線程
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM