LeetCode 哲學家進餐問題

2022-06-24 09:06:11 字數 4790 閱讀 3760

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

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

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

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

哲學家從 0 到 4 按 順時針 編號。請實現函式 void wantstoeat(philosopher, pickleftfork, pickrightfork, eat, putleftfork, putrightfork):

philosopher 哲學家的編號。

pickleftfork 和 pickrightfork 表示拿起左邊或右邊的叉子。

eat 表示吃麵。

putleftfork 和 putrightfork 表示放下左邊或右邊的叉子。

由於哲學家不是在吃麵就是在想著啥時候吃麵,所以思考這個方法沒有對應的**。

給你 5 個執行緒,每個都代表乙個哲學家,請你使用類的同乙個物件來模擬這個過程。在最後一次呼叫結束之前,可能會為同乙個哲學家多次呼叫該函式。

示例:輸入:n = 1

輸出:[[4,2,1],[4,1,1],[0,1,1],[2,2,1],[2,1,1],[2,0,3],[2,1,2],[2,2,2],[4,0,3],[4,1,2],[0,2,1],[4,2,2],[3,2,1],[3,1,1],[0,0,3],[0,1,2],[0,2,2],[1,2,1],[1,1,1],[3,0,3],[3,1,2],[3,2,2],[1,0,3],[1,1,2],[1,2,2]]

解釋:n 表示每個哲學家需要進餐的次數。

輸出陣列描述了叉子的控制和進餐的呼叫,它的格式如下:

output[i] = [a, b, c] (3個整數)

a:引用:@̶.̶g̶f̶u̶'̶ 、̶ ̶|

這個題目是防止死鎖,每個哲學家都拿起左手或右手,導致死鎖

1.第一種方法是設定乙個訊號量,當前哲學家會同時拿起左手和右手的叉子直至吃完。即有3 個人中,2 個人各自持有 2 個叉子,1 個人持有 1 個叉子,共計 5 個叉子。

用semaphore去實現上述的限制:semaphore eatlimit = new semaphore(4);

一共有5個叉子,視為5個reentrantlock,並將它們全放入1個陣列中。

設定編碼:

//限制 最多只有4個哲學家去持有叉子

private semaphore eatlimit = new semaphore(4);

public diningphilosophers()

// call the run() method of any runnable to execute its code

public void wantstoeat(int philosopher,

runnable pickleftfork,

runnable pickrightfork,

runnable eat,

runnable putleftfork,

runnable putrightfork) throws interruptedexception

}2.設定 1 個臨界區以實現 1 個哲學家 「同時」拿起左右 2 把叉子的效果。即進入臨界區之後,保證成功獲取到左右 2 把叉子並 執行相關**後,才退出臨界區。

與上一種的差別是「允許1個哲學家用餐」。方法2是在成功拿起左右叉子之後就退出臨界區,而「只讓1個哲學家就餐」是在拿起左右叉子 + 吃意麵 + 放下左右叉子一套流程走完之後才退出臨界區。

前者的情況可大概分為2種,舉具體例子說明(可參照上面給出的):

總之,第1種情況即先後進入臨界區的2位哲學家的左右叉子不存在競爭情況,因此先後進入臨界區的2位哲學家進入臨界區後都不用等待叉子,直接就餐。此時可視為2個哲學家在同時就餐(當然前1個哲學家有可能已經吃完了,但姑且當作是2個人同時就餐)。

第2種情況即先後進入臨界區的2位哲學家的左右叉子存在競爭情況(說明這2位哲學家的編號相鄰),因此後進入臨界區的哲學家還需要等待1只叉子,才能就餐。此時可視為只有1個哲學家在就餐。

至於「只允許1個哲學家就餐」的**,很好理解,每次嚴格地只讓1個哲學家就餐,由於過於嚴格,以至於都不需要將叉子視為reentrantlock。

方法2有一定的概率是「並行」,「只允許1個哲學家就餐」是嚴格的「序列」。

**:

class diningphilosophers ;

//讓 1個哲學家可以 「同時」拿起2個叉子(搞個臨界區)

private reentrantlock pickbothforks = new reentrantlock();

public diningphilosophers()

// call the run() method of any runnable to execute its code

public void wantstoeat(int philosopher,

runnable pickleftfork,

runnable pickrightfork,

runnable eat,

runnable putleftfork,

runnable putrightfork) throws interruptedexception

}

3.前面說過,該題的本質是考察 如何避免死鎖。

而當5個哲學家都左手持有其左邊的叉子 或 當5個哲學家都右手持有其右邊的叉子時,會發生死鎖。

故只需設計1個避免發生上述情況發生的策略即可。

即可以讓一部分哲學家優先去獲取其左邊的叉子,再去獲取其右邊的叉子;再讓剩餘哲學家優先去獲取其右邊的叉子,再去獲取其左邊的叉子。

**:

class diningphilosophers ;

public diningphilosophers()

// call the run() method of any runnable to execute its code

public void wantstoeat(int philosopher,

runnable pickleftfork,

runnable pickrightfork,

runnable eat,

runnable putleftfork,

runnable putrightfork) throws interruptedexception

//編號為奇數的哲學家,優先拿起右邊的叉子,再拿起左邊的叉子

else

pickleftfork.run(); //拿起左邊的叉子 的具體執行

pickrightfork.run(); //拿起右邊的叉子 的具體執行

eat.run(); //吃義大利面 的具體執行

putleftfork.run(); //放下左邊的叉子 的具體執行

putrightfork.run(); //放下右邊的叉子 的具體執行

locklist[leftfork].unlock(); //放下左邊的叉子

locklist[rightfork].unlock(); //放下右邊的叉子

}}

改進:改進**看3種解法(互斥鎖或volatile)

1.reentrantlock和synchronize關鍵字都是使用互斥量的重量級鎖,而volatile關鍵字相較於它們就比較「輕量」。

因此把reentrantlock陣列改為使用volatile修飾的boolean陣列。

ps: volatile要和原子操作搭配使用才能保證同步。

而對volatile變數賦 常量值 可看為是原子操作。

看著後面這種解法更清晰:

每個人都可以嘗試去吃東西,吃東西前嘗試去拿左邊的叉子和右邊的叉子,這樣就可以想到使用訊號量semaphore的tryacquire方法。

這裡競爭的資源是叉子,所以定義代表5個叉子的訊號量即可。

**:

class diningphilosophers 

}// call the run() method of any runnable to execute its code

public void wantstoeat(int philosopher,

runnable pickleftfork,

runnable pickrightfork,

runnable eat,

runnable putleftfork,

runnable putrightfork) throws interruptedexception else

} else }}

}

哲學家進餐問題

哲學家進餐問題 一 問題 5個哲學家圍坐在乙個圓桌上,每兩個哲學家之間都有乙隻筷子,哲學家平時進行思考,只有當他們飢餓時,才拿起筷子吃飯。規定每個哲學家只能先取其左邊筷子,然後取其右邊筷子,然後才可以吃飯。二 分析 每乙隻筷子都是乙個臨界資源,設定5個互斥訊號量。semaphore stcik 5 ...

哲學家進餐問題

一張圓桌上坐著5名哲學家,每兩個哲學家之間的桌上擺一根筷子,桌子的中間是一碗公尺飯,如圖所示。哲學家們傾注畢生精力用於思考和進餐,哲學家在思考時,並不影響他人。只有當哲學家飢餓的時候,才試圖拿起左 右兩根筷子 一根一根地拿起 如果筷子已在他人手上,則需等待。飢餓的哲學家只有同時拿到了兩根筷子才可以開...

哲學家進餐問題

哲學家進餐問題 五個哲學家共用一張圓桌,分別坐在周圍的五張椅子上,在桌子上有五隻碗和五隻筷子,他們的生活方式是交替地進行思考和進餐。平時,乙個哲學家進行思考,飢餓時便試圖取用其左右最靠近他的筷子,只有在他拿到兩隻筷子時才能進餐。進餐畢,放下筷子繼續思考 分析 放在桌子上的筷子是臨界資源,在一段時間內...