哲學家進餐

哲學家進餐避免死鎖

進餐條件

5 個沉默寡言的哲學家圍坐在圓桌前,每人面前一盤意麪。叉子放在哲學家之間的桌面上。(5 個哲學家,5 根叉子)

所有的哲學家都只會在思考和進餐兩種行爲間交替。哲學家只有同時拿到左邊和右邊的叉子才能吃到面,而同一根叉子在同一時間只能被一個哲學家使用。每個哲學家吃完麪後都需要把叉子放回桌面以供其他哲學家吃麪。只要條件允許,哲學家可以拿起左邊或者右邊的叉子,但在沒有同時拿到左右叉子時不能進食。

假設面的數量沒有限制,哲學家也能隨便吃,不需要考慮吃不吃得下。

設計一個進餐規則(並行算法)使得每個哲學家都不會捱餓;也就是說,在沒有人知道別人什麼時候想吃東西或思考的情況下,每個哲學家都可以在吃飯和思考之間一直交替下去。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/the-dining-philosophers
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

死鎖條件:5個哲學家同時拿起左邊叉子

PS:死鎖的 4個必要條件:

  • 互斥條件:一個資源每次只能被一個進程使用,即在一段時間內某 資源僅爲一個進程所佔有。此時若有其他進程請求該資源,則請求進程只能等待。
  • 請求與保持條件:進程已經保持了至少一個資源,但又提出了新的資源請求,而該資源 已被其他進程佔有,此時請求進程被阻塞,但對自己已獲得的資源保持不放。
  • 不可剝奪條件:進程所獲得的資源在未使用完畢之前,不能被其他進程強行奪走,即只能 由獲得該資源的進程自己來釋放(只能是主動釋放)。
  • 循環等待條件: 若干進程間形成首尾相接循環等待資源的關係。

作者:gfu
鏈接:https://leetcode-cn.com/problems/the-dining-philosophers/solution/1ge-semaphore-1ge-reentrantlockshu-zu-by-gfu/
來源:力扣(LeetCode) 著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

解決方式:
1.同時拿起兩邊叉子,否則不拿
2.順序拿起左邊叉,同時進餐人數不超過四人

方案一:同時拿起兩邊叉子,否則不拿
代碼如下:

void getFork(int i){
        Semaphore left=semaphores[i];
        Semaphore right=null;

        if(i==4){
            right=semaphores[0];
        }else {
            right=semaphores[i+1];
        }

        if(left.tryAcquire()){
            System.out.println(i+"拿到叉子左:"+i);
            if(right.tryAcquire()){
                System.out.println(i+"拿到叉子右:"+String.valueOf(i+1));

                execute(i);


                right.release();
                left.release();
                System.out.println(i+"釋放兩個叉子");
            }else {
                System.out.println(i+"釋放叉子左:"+i);
                left.release();

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }else {

        }
        int priority =Thread.currentThread().getPriority();

        Thread.currentThread().setPriority(2);

        Thread.yield();
        System.out.println(priority);

    }

方案二
順序拿起左邊叉,同時進餐人數不超過四人
1.設置拿叉子順序,(除非拿起所有叉子,否則不釋放鎖,一直阻塞)

void getFork2(int i){
        Semaphore left=semaphores[i];
        Semaphore right=null;

        if(i==4){
            right=semaphores[0];
        }else {
            right=semaphores[i+1];
        }

        try {
            left.acquire();
            System.out.println(i+"拿到叉子左:"+i);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        try {

            right.acquire();
            System.out.println(i+"拿到叉子右:"+i);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        execute(i);
        left.release();
        right.release();
    }

2.利用線程池保證同時執行的線程不超過四個

  ExecutorService executorService= Executors.newFixedThreadPool(5);

測試代碼如下

package threads;

import org.junit.Test;

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * @description:哲學家進餐
 * @author: HYW
 * @create: 2020-01-03 15:08
 */
public class DiningPhilosophers {
    int num=5;

    private Semaphore[] semaphores=new Semaphore[4];

    public DiningPhilosophers() {
        for (int i = 0; i < num; i++) {
            //每隻叉子只有1個
            semaphores[i] = new Semaphore(1);
        }

    }




    void eat(int i){
        i=i-1;
        while(true){
           //方案一
           //getFork(i);
           //方案二
            getFork2(i);
            Thread.yield();
            /*try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/

        }
    }
    void getFork2(int i){
        Semaphore left=semaphores[i];
        Semaphore right=null;

        if(i==4){
            right=semaphores[0];
        }else {
            right=semaphores[i+1];
        }

        try {
            left.acquire();
            System.out.println(i+"拿到叉子左:"+i);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        try {

            right.acquire();
            System.out.println(i+"拿到叉子右:"+i);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        execute(i);
        left.release();
        right.release();
    }
    void getFork(int i){
        Semaphore left=semaphores[i];
        Semaphore right=null;

        if(i==4){
            right=semaphores[0];
        }else {
            right=semaphores[i+1];
        }

        if(left.tryAcquire()){
            System.out.println(i+"拿到叉子左:"+i);
            if(right.tryAcquire()){
                System.out.println(i+"拿到叉子右:"+String.valueOf(i+1));

                execute(i);


                right.release();
                left.release();
                System.out.println(i+"釋放兩個叉子");
            }else {
                System.out.println(i+"釋放叉子左:"+i);
                left.release();

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }else {

        }
        int priority =Thread.currentThread().getPriority();

        Thread.currentThread().setPriority(2);

        Thread.yield();
        System.out.println(priority);

    }

    void execute(int i){
        System.out.println("哲學家"+ i+" 吃飯");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }
    public static void main(String[] args) {
        DiningPhilosophers demo=new DiningPhilosophers();
        ExecutorService executorService= Executors.newFixedThreadPool(4);

        executorService.execute(new Runnable() {
            @Override
            public void run() {
                demo.eat(1);
            }
        });
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                demo.eat(2);
            }
        });

        executorService.execute(new Runnable() {
            @Override
            public void run() {
                demo.eat(3);
            }
        });

        executorService.execute(new Runnable() {
            @Override
            public void run() {
                demo.eat(4);
            }
        });

        executorService.execute(new Runnable() {
            @Override
            public void run() {
                demo.eat(5);
            }
        });

        executorService.shutdown();
    }
}

注意

線程池會把超過最大線程數的任務,放在隊列種等待,所以第五位哲學家只有在前四位哲學家的線程吃完(停止)後,纔可以吃飯。
改進方式,增加信號量,Semaphore eatnerNum= new Semaphore(4);
保證同時只有四人進餐

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章