Java多線程之哲學(xué)家就餐問題詳解
一、題目
教材提供一個哲學(xué)家就餐問題的解決方案的框架。本問題要求通過pthreads 互斥鎖來實現(xiàn)這個解決方案。
哲學(xué)家 首先創(chuàng)建 5 個哲學(xué)家,每個用數(shù)字 0~4 來標(biāo)識。每個哲學(xué)家作為一個單獨的 線程運行。 可使用 Pthreads 創(chuàng)建線程。哲學(xué)家在思考和吃飯之間交替。為了模擬這兩種活動,請讓線程休眠 1 到 3 秒鐘。當(dāng)哲學(xué)家想要吃飯時,他調(diào)用函數(shù):
pickup_forks(int philosopher _number) 其中,philosopher _number
為想吃飯哲學(xué)家的數(shù)字。當(dāng)哲學(xué)家吃完后,他調(diào)用函數(shù):
return _forks(int philosopher _number)
Pthreads 條件變量 Pthreads 條件變量使用數(shù)據(jù)類型 pthread_cond_t,采用函數(shù) pthread cond
init()初始化。以下代碼創(chuàng)建并初始化條件變量及其關(guān)聯(lián)的互斥鎖:
pthread _mutex_ t mutex; pthread_ cond_ t cond _var; pthread _mutex_ init(&mutex,NULL); pthread _cond_ init(&cond _var,NULL);
函數(shù) pthread_cond_wait()用于等待條件變量。下面的代碼采用 Pthreads條件變量,說明線程如何等待條件 a==b 變?yōu)?br /> true。
pthread _mutex_ lock(&mutex); while (a != b) pthread_cond _wait(&mutex, &cond var); pthread _mutex_ unlock(&mutex);
與條件變量關(guān)聯(lián)的互斥鎖在調(diào)用pthread_cond_wait()函數(shù)之前,應(yīng)加鎖,因為保護條件語句中的數(shù)據(jù),避免競爭條件。一旦獲得鎖,線程可以檢查條件。如果條件不成立,然后線程調(diào)用 pthread_cond_wait(),傳遞互斥鎖和條件變量作為參數(shù)。調(diào)用pthread_cond_wait()釋放互斥鎖,從而允許另一個線程訪問共享變量,也可更新其值,以便條件語句的計算結(jié)果為真。(為了防止程序錯誤,重要的是將條件語句放在循環(huán)中,以便在被喚醒后重新檢查條件。)修改共享數(shù)據(jù)的線程可以調(diào)用 pthread_cond_signal()函數(shù),從而喚醒一個等待條件變量的線程。這個代碼如下:
pthread_ mutex_ lock(&mutex); a = b; pthread_ cond_ signal(&cond var); pthread _mutex_ unlock(&mutex);
需要注意的是,調(diào)用pthread_cond_signal()不會釋放互斥鎖。隨后調(diào)用pthread_mutex_unlock()釋放互斥鎖。一旦互斥鎖被釋放,喚醒線程成為互斥鎖的所有者,并將控制權(quán)返回到對pthread cond wait()的調(diào)用。
二、題目解析
題目是書上的哲學(xué)家就餐問題,要求用互斥鎖解決。
這題的關(guān)鍵在于筷子這個資源的合理使用,書上給出了三種不同的解決思路:

其實三種方法的核心就是為了解決死鎖問題。
什么是死鎖呢?
用專業(yè)點的話說就是:一組互相競爭資源的線程因互相等待,導(dǎo)致“永久”阻塞的現(xiàn)象。
說白了,就是你拿了我想要拿的資源,我拿了你想要拿的資源,而雙方各執(zhí)一詞,導(dǎo)致一直無法解決問題。
那我的思路就是:
雙方各退一步,當(dāng)發(fā)現(xiàn)我想要的資源不夠我完成我所需的事情時,那就把之前拿到的資源放回。這樣就不會導(dǎo)致雙方互相等待導(dǎo)致死鎖的情況。
當(dāng)然思路不止一種,在極客時間的Java并發(fā)編程實戰(zhàn)中有講到死鎖發(fā)生的條件:
只有以下這四個條件都發(fā)生時才會出現(xiàn)死鎖:
互斥,共享資源 X 和 Y 只能被一個線程占用;占有且等待,線程 T1 已經(jīng)取得共享資源 X,在等待共享資源 Y 的時候,不釋放共享資源 X;不可搶占,其他線程不能強行搶占線程 T1 占有的資源;循環(huán)等待,線程 T1 等待線程 T2 占有的資源,線程 T2 等待線程 T1 占有的資源,就是循環(huán)等待。
如何解決死鎖呢?打破任意一個條件即可。
其中,互斥這個條件我們沒有辦法破壞,因為我們用鎖為的就是互斥。不過其他三個條件都是有辦法破壞掉的,到底如何做呢?
對于“占用且等待”這個條件,我們可以一次性申請所有的資源,這樣就不存在等待了。對于“不可搶占”這個條件,占用部分資源的線程進(jìn)一步申請其他資源時,如果申請不到,可以主動釋放它占有的資源,這樣不可搶占這個條件就破壞掉了。對于“循環(huán)等待”這個條件,可以靠按序申請資源來預(yù)防。所謂按序申請,是指資源是有線性順序的,申請的時候可以先申請資源序號小的,再申請資源序號大的,這樣線性化后自然就不存在循環(huán)了。
好了,回到正題上來,既然我們的思路就是拿不到資源就選擇退讓,我們可以借用Java里的ReentrantLock類中trylock()方法實現(xiàn),該方法的作用就是嘗試鎖住該資源。和lock()方法不同的是,如果該資源已經(jīng)上鎖,那么線程不會阻塞,而是返回flase,表示無法上鎖,當(dāng)然還可以加入?yún)?shù)讓它再嘗試幾秒,比如:
lock.tryLock(1, TimeUnit.SECONDS));
而具體用法就是
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
...
} finally {
lock.unlock();
}
}
三、代碼實現(xiàn)
package com.dreamchaser.concurrent;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 哲學(xué)家就餐問題
*/
public class Proj3_02 {
static final Lock[] locks=new Lock[5];
static {
for (int i=0;i<locks.length;i++){
locks[i]=new ReentrantLock();
}
}
public static void main(String[] args) {
Philosopher philosopher0=new Philosopher("張三",1000,0);
Philosopher philosopher1=new Philosopher("李四",800,1);
Philosopher philosopher2=new Philosopher("王五",400,2);
Philosopher philosopher3=new Philosopher("jhl",2000,3);
Philosopher philosopher4=new Philosopher("ghlcl",2000,4);
philosopher0.start();
philosopher1.start();
philosopher2.start();
philosopher3.start();
philosopher4.start();
//死循環(huán),防止主線程退出導(dǎo)致進(jìn)程關(guān)閉
while (true){}
}
static class Philosopher extends Thread{
private String name;
private long time;
private int num;
public Philosopher(String name, long time, int num) {
this.name = name;
this.time = time;
this.num = num;
}
@Override
public void run() {
while (true){
System.out.println(num+"號哲學(xué)家"+name+"正在思考...");
//模擬思考的過程
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(num+"號哲學(xué)家"+name+"餓了,想來吃飯...");
if (locks[num].tryLock()){
try {
System.out.println(num+"號哲學(xué)家"+name+"拿到了左邊的筷子!");
if (locks[(num+1)%5].tryLock()){
try {
System.out.println(num+"號哲學(xué)家"+name+"拿到了右邊的筷子!");
System.out.println(num+"號哲學(xué)家"+name+"開始吃飯!");
//模擬哲學(xué)家吃飯的過程
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(num+"號哲學(xué)家"+name+"放下了右邊的筷子!");
locks[(num+1)%5].unlock();
}
}else {
System.out.println(num+"號哲學(xué)家"+name+"沒拿到了右邊的筷子!被迫思考...");
}
}finally {
System.out.println(num+"號哲學(xué)家"+name+"放下了左邊的筷子!");
locks[num].unlock();
}
}else {
System.out.println(num+"號哲學(xué)家"+name+"沒拿到了左邊的筷子!被迫思考...");
}
System.out.println(num+"號哲學(xué)家"+name+"思考ing...");
//模擬思考過程
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
四、運行效果截圖


五、結(jié)語
以上是我的實現(xiàn)思路。希望我的思路能個各位有所幫助,當(dāng)然,如果有什么意見或者建議的,歡迎在評論區(qū)評論。
到此這篇關(guān)于Java多線程之哲學(xué)家就餐問題詳解的文章就介紹到這了,更多相關(guān)Java哲學(xué)家就餐詳解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- java多線程創(chuàng)建及線程安全詳解
- Java多線程之線程同步
- Java多線程之ReentrantReadWriteLock源碼解析
- Java多線程之線程的創(chuàng)建
- Java多線程之synchronized關(guān)鍵字的使用
- Java多線程之Disruptor入門
- Java基礎(chǔ)之多線程的三種實現(xiàn)方式
- Java多線程之Park和Unpark原理
- Java多線程之簡單模擬售票功能
- Java實現(xiàn)UDP多線程在線咨詢
- 基于Java網(wǎng)絡(luò)編程和多線程的多對多聊天系統(tǒng)
- java的多線程高并發(fā)詳解
- 教你如何使用Java多線程編程LockSupport工具類
- Java多線程之線程池七個參數(shù)詳解
- 總結(jié)java多線程之互斥與同步解決方案
相關(guān)文章
Spring?WebMVC初始化Controller流程詳解
這篇文章主要介紹了Spring?WebMVC初始化Controller流程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02
IDEA創(chuàng)建Maven項目一直顯示正在加載的問題及解決
這篇文章主要介紹了IDEA創(chuàng)建Maven項目一直顯示正在加載的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
基于Java實現(xiàn)計數(shù)排序,桶排序和基數(shù)排序
這篇文章主要為大家詳細(xì)介紹了計數(shù)排序,桶排序和基數(shù)排序的多種語言的實現(xiàn)(JavaScript、Python、Go語言、Java),感興趣的小伙伴可以了解一下2022-12-12
springboot 整合druid數(shù)據(jù)庫密碼加密功能的實現(xiàn)代碼
這篇文章主要介紹了springboot 整合druid數(shù)據(jù)庫密碼加密功能的實現(xiàn)代碼,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01
詳解java操作Redis數(shù)據(jù)庫的redis工具(RedisUtil,jedis工具JedisUtil,JedisPoo
這篇文章主要介紹了java操作Redis數(shù)據(jù)庫的redis工具,包括RedisUtil,jedis工具JedisUtil,JedisPoolUtil工具,本文通過實例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-08-08
Idea創(chuàng)建多模塊maven聚合項目的實現(xiàn)
這篇文章主要介紹了Idea創(chuàng)建多模塊maven聚合項目的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12

