哲學家進餐避免死鎖
進餐條件
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);
保證同時只有四人進餐