- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) >
- Java多線(xiàn)程之哲學(xué)家就餐問(wèn)題詳解
教材提供一個(gè)哲學(xué)家就餐問(wèn)題的解決方案的框架。本問(wèn)題要求通過(guò)pthreads 互斥鎖來(lái)實(shí)現這個(gè)解決方案。
哲學(xué)家 首先創(chuàng )建 5 個(gè)哲學(xué)家,每個(gè)用數字 0~4 來(lái)標識。每個(gè)哲學(xué)家作為一個(gè)單獨的 線(xiàn)程運行。 可使用 Pthreads 創(chuàng )建線(xiàn)程。哲學(xué)家在思考和吃飯之間交替。為了模擬這兩種活動(dòng),請讓線(xiàn)程休眠 1 到 3 秒鐘。當哲學(xué)家想要吃飯時(shí),他調用函數:
pickup_forks(int philosopher _number) 其中,philosopher _number
為想吃飯哲學(xué)家的數字。當哲學(xué)家吃完后,他調用函數:
return _forks(int philosopher _number)
Pthreads 條件變量 Pthreads 條件變量使用數據類(lèi)型 pthread_cond_t,采用函數 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);
函數 pthread_cond_wait()用于等待條件變量。下面的代碼采用 Pthreads條件變量,說(shuō)明線(xiàn)程如何等待條件 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)的互斥鎖在調用pthread_cond_wait()函數之前,應加鎖,因為保護條件語(yǔ)句中的數據,避免競爭條件。一旦獲得鎖,線(xiàn)程可以檢查條件。如果條件不成立,然后線(xiàn)程調用 pthread_cond_wait(),傳遞互斥鎖和條件變量作為參數。調用pthread_cond_wait()釋放互斥鎖,從而允許另一個(gè)線(xiàn)程訪(fǎng)問(wèn)共享變量,也可更新其值,以便條件語(yǔ)句的計算結果為真。(為了防止程序錯誤,重要的是將條件語(yǔ)句放在循環(huán)中,以便在被喚醒后重新檢查條件。)修改共享數據的線(xiàn)程可以調用 pthread_cond_signal()函數,從而喚醒一個(gè)等待條件變量的線(xiàn)程。這個(gè)代碼如下:
pthread_ mutex_ lock(&mutex); a = b; pthread_ cond_ signal(&cond var); pthread _mutex_ unlock(&mutex);
需要注意的是,調用pthread_cond_signal()不會(huì )釋放互斥鎖。隨后調用pthread_mutex_unlock()釋放互斥鎖。一旦互斥鎖被釋放,喚醒線(xiàn)程成為互斥鎖的所有者,并將控制權返回到對pthread cond wait()的調用。
題目是書(shū)上的哲學(xué)家就餐問(wèn)題,要求用互斥鎖解決。
這題的關(guān)鍵在于筷子這個(gè)資源的合理使用,書(shū)上給出了三種不同的解決思路:
其實(shí)三種方法的核心就是為了解決死鎖問(wèn)題。
什么是死鎖呢?
用專(zhuān)業(yè)點(diǎn)的話(huà)說(shuō)就是:一組互相競爭資源的線(xiàn)程因互相等待,導致“永久”阻塞的現象。
說(shuō)白了,就是你拿了我想要拿的資源,我拿了你想要拿的資源,而雙方各執一詞,導致一直無(wú)法解決問(wèn)題。
那我的思路就是:
雙方各退一步,當發(fā)現我想要的資源不夠我完成我所需的事情時(shí),那就把之前拿到的資源放回。這樣就不會(huì )導致雙方互相等待導致死鎖的情況。
當然思路不止一種,在極客時(shí)間的Java并發(fā)編程實(shí)戰中有講到死鎖發(fā)生的條件:
只有以下這四個(gè)條件都發(fā)生時(shí)才會(huì )出現死鎖:
互斥,共享資源 X 和 Y 只能被一個(gè)線(xiàn)程占用;占有且等待,線(xiàn)程 T1 已經(jīng)取得共享資源 X,在等待共享資源 Y 的時(shí)候,不釋放共享資源 X;不可搶占,其他線(xiàn)程不能強行搶占線(xiàn)程 T1 占有的資源;循環(huán)等待,線(xiàn)程 T1 等待線(xiàn)程 T2 占有的資源,線(xiàn)程 T2 等待線(xiàn)程 T1 占有的資源,就是循環(huán)等待。
如何解決死鎖呢?打破任意一個(gè)條件即可。
其中,互斥這個(gè)條件我們沒(méi)有辦法破壞,因為我們用鎖為的就是互斥。不過(guò)其他三個(gè)條件都是有辦法破壞掉的,到底如何做呢?
對于“占用且等待”這個(gè)條件,我們可以一次性申請所有的資源,這樣就不存在等待了。對于“不可搶占”這個(gè)條件,占用部分資源的線(xiàn)程進(jìn)一步申請其他資源時(shí),如果申請不到,可以主動(dòng)釋放它占有的資源,這樣不可搶占這個(gè)條件就破壞掉了。對于“循環(huán)等待”這個(gè)條件,可以靠按序申請資源來(lái)預防。所謂按序申請,是指資源是有線(xiàn)性順序的,申請的時(shí)候可以先申請資源序號小的,再申請資源序號大的,這樣線(xiàn)性化后自然就不存在循環(huán)了。
好了,回到正題上來(lái),既然我們的思路就是拿不到資源就選擇退讓?zhuān)覀兛梢越栌肑ava里的ReentrantLock類(lèi)中trylock()方法實(shí)現,該方法的作用就是嘗試鎖住該資源。和lock()方法不同的是,如果該資源已經(jīng)上鎖,那么線(xiàn)程不會(huì )阻塞,而是返回flase,表示無(wú)法上鎖,當然還可以加入參數讓它再?lài)L試幾秒,比如:
lock.tryLock(1, TimeUnit.SECONDS));
而具體用法就是
if (lock.tryLock(1, TimeUnit.SECONDS)) { try { ... } finally { lock.unlock(); } }
package com.dreamchaser.concurrent; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 哲學(xué)家就餐問(wèn)題 */ 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),防止主線(xiàn)程退出導致進(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+"正在思考..."); //模擬思考的過(guò)程 try { Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(num+"號哲學(xué)家"+name+"餓了,想來(lái)吃飯..."); 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+"開(kāi)始吃飯!"); //模擬哲學(xué)家吃飯的過(guò)程 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+"沒(méi)拿到了右邊的筷子!被迫思考..."); } }finally { System.out.println(num+"號哲學(xué)家"+name+"放下了左邊的筷子!"); locks[num].unlock(); } }else { System.out.println(num+"號哲學(xué)家"+name+"沒(méi)拿到了左邊的筷子!被迫思考..."); } System.out.println(num+"號哲學(xué)家"+name+"思考ing..."); //模擬思考過(guò)程 try { Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
以上是我的實(shí)現思路。希望我的思路能個(gè)各位有所幫助,當然,如果有什么意見(jiàn)或者建議的,歡迎在評論區評論。
到此這篇關(guān)于Java多線(xiàn)程之哲學(xué)家就餐問(wèn)題詳解的文章就介紹到這了,更多相關(guān)Java哲學(xué)家就餐詳解內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。
Copyright ? 2009-2021 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 珠海市特網(wǎng)科技有限公司 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站