java實現哲學家進餐問題,及其死鎖問題的解決
阿新 • • 發佈:2018-12-31
首先我們來了解一下哲學家進餐問題的背景:
話說有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();
}
}
}
}
}
好了部落格寫完了,你有沒有學到點東西了。