哲学家就餐与死锁问题,死锁产生的条件以及解决方案
請(qǐng)結(jié)合經(jīng)典案例-哲學(xué)家就餐,來談?wù)勀銓?duì)死鎖的理解,以及怎么預(yù)防和解除死鎖?
哲學(xué)家就餐
描述:在一張圓桌上,有n個(gè)哲學(xué)家,n支筷子,他們的生活方式只是交替地進(jìn)行思考和進(jìn)餐,饑餓時(shí)便試圖取其左、右最靠近他的筷子,只有在他拿到兩支筷子時(shí)才能進(jìn)餐,進(jìn)餐完畢,放下筷子又繼續(xù)思考。
根據(jù)描述,實(shí)現(xiàn)代碼如下:
import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit;public class Question17 {public static void main(String[] args) {ExecutorService exec = Executors.newCachedThreadPool();int sum = 5;Chopstick[] chopsticks = new Chopstick[sum];for (int i = 0; i < sum; i++) {chopsticks[i] = new Chopstick(i);}for (int j = 0; j < sum; j++) {exec.execute(new Philosopher(chopsticks[j], chopsticks[(j + 1) % sum], j));}} }// 筷子 class Chopstick {// 筷子位置??private int id;// 狀態(tài)private boolean isUsed = false;public Chopstick(int id) {this.id = id;}// 拿取public synchronized void take() throws InterruptedException {while (isUsed) {wait();}System.err.println(this + " 被使用");isUsed = true;}// 放下public synchronized void drop() {isUsed = false;System.err.println(this + " 被放下");notifyAll();}@Overridepublic String toString() {return "筷子[" + id + "]";} }// 哲學(xué)家 class Philosopher implements Runnable {private Chopstick left;private Chopstick right;private int id;private Random rand = new Random();public Philosopher(Chopstick left, Chopstick right, int id) {this.left = left;this.right = right;this.id = id;}@Overridepublic void run() {while (!Thread.interrupted()) {try {think();System.out.println(this + " 想吃飯!");eat();} catch (InterruptedException e) {System.err.println(this + " InterruptedException");}}}// 思考private void think() throws InterruptedException {System.out.println(this + " 思考...");TimeUnit.MILLISECONDS.sleep(rand.nextInt(1) * 100);}// 吃飯private void eat() throws InterruptedException {left.take();right.take();System.out.println(this + " 正在吃飯...");TimeUnit.MILLISECONDS.sleep(rand.nextInt(2) * 100);left.drop();right.drop();}@Overridepublic String toString() {return "哲學(xué)家[" + id + "]";} }
通過運(yùn)行結(jié)果,我們可以發(fā)現(xiàn),到最后,沒有一個(gè)哲學(xué)家能過同時(shí)獲取兩只筷子吃飯。
二、為什么會(huì)產(chǎn)生死鎖
死鎖問題被認(rèn)為是線程/進(jìn)程間切換消耗系統(tǒng)性能的一種極端情況。在死鎖時(shí),線程/進(jìn)程間相互等待資源,而又不釋放自身的資源,導(dǎo)致無窮無盡的等待,其結(jié)果是任務(wù)永遠(yuǎn)無法執(zhí)行完成。哲學(xué)家問題便是線程資源競爭導(dǎo)致的死鎖現(xiàn)象,在程序運(yùn)行一段時(shí)間后,程序所處的狀態(tài)是n位哲學(xué)家(n個(gè)線程)都各自獲取了一只筷子的狀態(tài),此時(shí)所有哲學(xué)家都想獲取第二只筷子去吃飯,但是共享資源n只筷子已經(jīng)都被n位哲學(xué)家握在手里了,彼此想要的筷子都在其他哲學(xué)家手中,又沒有機(jī)制能讓任何哲學(xué)家放棄握在手中的筷子,從而照成了所有哲學(xué)家(所有線程)都在等待其他人手中資源的死鎖問題。
產(chǎn)生死鎖的四個(gè)必要條件:?
?
三、死鎖的解除與預(yù)防
一般解決死鎖的途徑分為死鎖的預(yù)防,避免,檢測與恢復(fù)這三種。
死鎖的預(yù)防是要求線程/進(jìn)程申請(qǐng)資源時(shí)遵循某種協(xié)議,從而打破產(chǎn)生死鎖的四個(gè)必要條件中的一個(gè)或幾個(gè),保證系統(tǒng)不會(huì)進(jìn)入死鎖狀態(tài)。
死鎖的避免不限制線程/進(jìn)程有關(guān)申請(qǐng)資源的命令,而是對(duì)線程/進(jìn)程所發(fā)出的每一個(gè)申請(qǐng)資源命令加以動(dòng)態(tài)地檢查,并根據(jù)檢查結(jié)果決定是否進(jìn)行資源分配。
死鎖檢測與恢復(fù)是指系統(tǒng)設(shè)有專門的機(jī)構(gòu),當(dāng)死鎖發(fā)生時(shí),該機(jī)構(gòu)能夠檢測到死鎖發(fā)生的位置和原因,并能通過外力破壞死鎖發(fā)生的必要條件,從而使得并發(fā)進(jìn)程從死鎖狀態(tài)中恢復(fù)出來。
對(duì)于java程序來說,產(chǎn)生死鎖時(shí),我們可以用jvisualvm/jstack來分析程序產(chǎn)生的死鎖原因,根治死鎖問題。
總結(jié)
以上是生活随笔為你收集整理的哲学家就餐与死锁问题,死锁产生的条件以及解决方案的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 以Java的视角来聊聊BIO、NIO与A
- 下一篇: 过滤器跟拦截器的区别