1. 程式人生 > >java實現哲學家進餐問題,及其死鎖問題的解決

java實現哲學家進餐問題,及其死鎖問題的解決

首先我們來了解一下哲學家進餐問題的背景:
話說有5個哲學家圍在一張桌子上吃飯,桌上只有5g根筷子,一個要吃飯必須的得有兩根筷子,哲學家要吃飯時總是先拿起左邊的筷子,在拿起右邊的筷子,這樣最佳的情況是可同時有兩人可以進餐,最壞的情況是大家都拿起了左邊的筷子,大家都沒得吃。哲學家吃完時會停下來思考一段時間,等餓了在吃。
下面我們來看一下java如何模擬哲學家進餐問題

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
//哲學家吃飯問題
public class ETTest { //建立大小為5的訊號量陣列,模擬5根筷子 static Semaphore[] arry=new Semaphore[5]; public static void main(String[] args) { //建立一個5個執行緒的執行緒池 ExecutorService es=Executors.newFixedThreadPool(5); //初始化訊號量 for(int i=0;i<5;i++){ arry[i]=new Semaphore(1
,true); } //建立5個哲學家 但這樣有可能會產生死鎖問題 for(int i=0;i<5;i++){ es.execute(new ActionRunnable(i)); } } //第i+1號哲學家的活動過程 static class ActionRunnable implements Runnable{ private int i=0; ActionRunnable(int i){ this.i=i; } @Override public
void run() { while(!Thread.interrupted()){ try { arry[i].acquire(); //請求右邊的筷子 arry[(i+1)%5].acquire(); //吃飯 System.out.println("我是哲學家"+(i+1)+"號我在吃飯"); //釋放左手的筷子 arry[i].release(); //釋放右手的筷子 arry[(i+1)%5].release(); //哲學家開始思考 System.out.println("我是哲學家"+(i+1)+"號我吃飽了我要開始思考了"); //通知cpu 將排程權讓給其他哲學家執行緒 Thread.yield(); //思考1秒 //把休眠關閉,造成死鎖的概率就會增加 //Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }

我們在讓哲學家吃完後,不進行思考,立即進入下一輪的搶筷子競爭中,以增加死鎖出現的概率。
執行上面程式碼後我們會發現程式死鎖了
這樣的死鎖問題該如何解決了。
有以下倆種解決策略
第一種:
讓最多隻有四個哲學家可以同時拿起左邊的筷子,這樣我們就可以保證,最差情況都有至少有一個哲學家可以進餐成功,進餐成功後就會釋放筷子資源,這樣就不會造成死鎖啦。
那java程式碼是如實現的了。
看下面我基於上面的程式碼做出的修改。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
//哲學家吃飯問題
public class ETTest2 {
    //建立大小為5的訊號量陣列,模擬5根筷子
    static Semaphore[] arry=new Semaphore[5];
    //定義一個值為4的訊號量,代表最多隻能有四個哲學家拿起左邊的筷子
      static  Semaphore leftCount=new Semaphore(4,true);
    public static void main(String[] args) {
        //建立一個5個執行緒的執行緒池
        ExecutorService es=Executors.newFixedThreadPool(5);

        //初始化訊號量
        for(int i=0;i<5;i++){
            arry[i]=new Semaphore(1,true);
        }
        //建立5個哲學家 但這樣有可能會產生死鎖問題
        for(int i=0;i<5;i++){
            es.execute(new ActionRunnable(i));
        }

    }
    //第i+1號哲學家的活動過程
    static class ActionRunnable implements Runnable{
        private int i=0;
        ActionRunnable(int i){
            this.i=i;
        }

        @Override
        public void run() {
            while(!Thread.interrupted()){
                try {
                    //看拿起左邊筷子的執行緒數是否已滿,可以,則能拿起左邊筷子的執行緒數減一,不能則等待
                    leftCount.acquire();
                    arry[i].acquire();
                    //請求右邊的筷子
                    arry[(i+1)%5].acquire();
                    //吃飯
                    System.out.println("我是哲學家"+(i+1)+"號我在吃飯");
                    //釋放左手的筷子
                    arry[i].release();
                    //能拿起左邊筷子的執行緒數量加一
                    leftCount.release();
                    //釋放右手的筷子
                    arry[(i+1)%5].release();
                     //哲學家開始思考
                    System.out.println("我是哲學家"+(i+1)+"號我吃飽了我要開始思考了");
                    //通知cpu 將排程權讓給其他哲學家執行緒
                    Thread.yield();
                    //思考1秒
                    //把休眠關閉,造成死鎖的概率就會增加 
                    //Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }


            }

        }

    }

}

第二種策略為,奇數號的哲學家先拿起左邊的筷子,在拿起右邊的筷子。偶數號的哲學家先拿起右邊的筷子,再拿起左邊的筷子,則以就變成,只有1號和2號哲學家會同時競爭1號的筷子,3號和4四號的哲學家會同時競爭3號的筷子,即5位哲學家會先競爭奇數號的筷子,再去競爭偶數號的筷子,最後總會有一個哲學家可以進餐成功。
下面來看一下程式碼是如何實現的

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
//哲學家進餐問題
public class ETTest {
    //建立大小為5的訊號量陣列,模擬5根筷子
    static Semaphore[] arry=new Semaphore[5];
    public static void main(String[] args) {
        //建立一個5個執行緒的執行緒池
        ExecutorService es=Executors.newFixedThreadPool(5);

        //初始化訊號量
        for(int i=0;i<5;i++){
            arry[i]=new Semaphore(1,true);
        }
        //建立5個哲學家 但這樣有可能會產生死鎖問題
        for(int i=0;i<5;i++){
            es.execute(new ActionRunnable(i));
        }

    }
    //第i+1號哲學家的活動過程
    static class ActionRunnable implements Runnable{
        private int i=0;
        ActionRunnable(int i){
            this.i=i;
        }

        @Override
        public void run() {
            while(!Thread.interrupted()){
                try {
                    if((i+1)%2!=0){
                    //奇數號哲學家
                    //請求左邊的筷子
                    arry[i].acquire();
                    //請求右邊的筷子
                    arry[(i+1)%5].acquire();
                    }else{
                    //偶數號哲學家
                        //請求右邊的筷子
                        arry[(i+1)%5].acquire();
                        //再請求左邊的筷子
                        arry[i].acquire();
                    }
                    //吃飯
                    System.out.println("我是哲學家"+(i+1)+"號我在吃飯");

                    if((i+1)%2!=0){
                    //奇數號哲學家
                    //釋放左手的筷子
                    arry[i].release();
                    //釋放右手的筷子
                    arry[(i+1)%5].release();
                    }else{
                    //偶數號的哲學家
                        arry[(i+1)%5].release();
                        arry[i].release();
                    }
                     //哲學家開始思考
                    System.out.println("我是哲學家"+(i+1)+"號我吃飽了我要開始思考了");
                    //通知cpu 將排程權讓給其他哲學家執行緒
                    Thread.yield();
                    //思考1秒
                    //把休眠關閉,造成死鎖的概率就會增加 
                    //Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }


            }

        }

    }

}

好了部落格寫完了,你有沒有學到點東西了。