成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

經(jīng)典的哲學(xué)家就餐問題,忘記的再回憶回憶

開發(fā) 前端
有五把叉子供他們共享(1 - 5),為了能夠進(jìn)食,一位哲學(xué)家需要雙手都持有叉子。用餐完畢后,他放下兩把叉子,然后其他哲學(xué)家可以拿起它們,重復(fù)相同的循環(huán)。目標(biāo)是設(shè)計(jì)一種方案,幫助哲學(xué)家們實(shí)現(xiàn)進(jìn)食和思考的目標(biāo),而不會(huì)餓死。

哲學(xué)家就餐問題是用于描述多線程環(huán)境中的同步問題并闡釋解決這些問題的技術(shù)的經(jīng)典問題之一。該問題最初由Dijkstra提出,是關(guān)于計(jì)算機(jī)訪問磁帶驅(qū)動(dòng)器外設(shè)的情況。當(dāng)前的表述由Tony Hoare給出,他還發(fā)明了快速排序算法。

在本文中,我們將分析這個(gè)著名的問題并編寫一個(gè)常用的解決方案。

一、問題描述

經(jīng)典的哲學(xué)家就餐問題經(jīng)典的哲學(xué)家就餐問題

上述圖表展示了哲學(xué)家就餐問題。有五位沉默的哲學(xué)家(P1 - P5)圍坐在一張圓形餐桌旁,他們一生都在進(jìn)食和思考。

有五把叉子供他們共享(1 - 5),為了能夠進(jìn)食,一位哲學(xué)家需要雙手都持有叉子。用餐完畢后,他放下兩把叉子,然后其他哲學(xué)家可以拿起它們,重復(fù)相同的循環(huán)。

目標(biāo)是設(shè)計(jì)一種方案,幫助哲學(xué)家們實(shí)現(xiàn)進(jìn)食和思考的目標(biāo),而不會(huì)餓死。

二、解決方案

最初的解決方案是讓每個(gè)哲學(xué)家遵循以下邏輯:

while (true) {
    // 初始狀態(tài),思考人生、宇宙及萬物
    think();

    // 停止思考,感到饑餓
    pick_up_left_fork();
    pick_up_right_fork();
    eat();
    put_down_right_fork();
    put_down_left_fork();

    // 不再饑餓,繼續(xù)思考!
}

如上述偽代碼所述,每個(gè)哲學(xué)家最初都在思考。一段時(shí)間后,哲學(xué)家感到饑餓并希望進(jìn)食。

此時(shí),他拿起左右兩邊的叉子,一旦拿到兩把叉子,就開始進(jìn)食。進(jìn)食完成后,哲學(xué)家放下叉子,以便鄰居可以使用。

三、實(shí)現(xiàn)

我們將每個(gè)哲學(xué)家建模為實(shí)現(xiàn)Runnable接口的類,以便可以作為單獨(dú)的線程運(yùn)行它們。每個(gè)哲學(xué)家都可以訪問其左右兩側(cè)的兩把叉子:

public class Philosopher implements Runnable {

    // 此哲學(xué)家左右兩側(cè)的叉子
    private Object leftFork;
    private Object rightFork;

    public Philosopher(Object leftFork, Object rightFork) {
        this.leftFork = leftFork;
        this.rightFork = rightFork;
    }

    @Override
    public void run() {
        // 待填充此方法
    }
}

我們還有一個(gè)方法,用于指示哲學(xué)家執(zhí)行某個(gè)動(dòng)作——進(jìn)食、思考或獲取叉子準(zhǔn)備進(jìn)食:

public class Philosopher implements Runnable {

    // 成員變量、標(biāo)準(zhǔn)構(gòu)造函數(shù)

    private void doAction(String action) throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + " " + action);
        Thread.sleep(((int) (Math.random() * 100)));
    }

    // 之前編寫的其他方法
}

如上述代碼所示,每個(gè)動(dòng)作通過隨機(jī)暫停調(diào)用線程一段時(shí)間來模擬,這樣執(zhí)行順序就不僅僅由時(shí)間決定。

現(xiàn)在,我們實(shí)現(xiàn)哲學(xué)家的核心邏輯。

為了模擬獲取叉子,我們需要鎖定它,以確保沒有兩個(gè)哲學(xué)家線程同時(shí)獲取它。

為了實(shí)現(xiàn)這一點(diǎn),我們使用synchronized關(guān)鍵字獲取叉子對(duì)象的內(nèi)部監(jiān)視器,并防止其他線程執(zhí)行相同操作?,F(xiàn)在我們繼續(xù)在Philosopher類中實(shí)現(xiàn)run()方法:

public class Philosopher implements Runnable {

    // 之前定義的成員變量、方法

    @Override
    public void run() {
        try {
            while (true) {
                // 思考
                doAction(System.nanoTime() + ": Thinking");
                synchronized (leftFork) {
                    doAction(System.nanoTime() + ": Picked up left fork");
                    synchronized (rightFork) {
                        // 進(jìn)食
                        doAction(System.nanoTime() + ": Picked up right fork - eating");
                        doAction(System.nanoTime() + ": Put down right fork");
                    }
                    // 回到思考狀態(tài)
                    doAction(System.nanoTime() + ": Put down left fork. Back to thinking");
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return;
        }
    }
}

此方案準(zhǔn)確實(shí)現(xiàn)了前面描述的內(nèi)容:哲學(xué)家思考一段時(shí)間后決定進(jìn)食。

之后,他獲取左右兩邊的叉子并開始進(jìn)食。用餐完畢后,他放下叉子。我們還為每個(gè)動(dòng)作添加了時(shí)間戳,這將有助于我們了解事件發(fā)生的順序。

為了啟動(dòng)整個(gè)過程,我們編寫一個(gè)Main函數(shù),創(chuàng)建5個(gè)哲學(xué)家線程并啟動(dòng)它們:

public class DiningPhilosophers {

    public static void main(String[] args) throws Exception {

        Philosopher[] philosophers = new Philosopher[5];
        Object[] forks = new Object[philosophers.length];

        for (int i = 0; i < forks.length; i++) {
            forks[i] = new Object();
        }

        for (int i = 0; i < philosophers.length; i++) {
            Object leftFork = forks[i];
            Object rightFork = forks[(i + 1) % forks.length];

            philosophers[i] = new Philosopher(leftFork, rightFork);

            Thread t = new Thread(philosophers[i], "Philosopher " + (i + 1));
            t.start();
        }
    }
}

我們將每個(gè)叉子建模為通用的Java對(duì)象,并創(chuàng)建與哲學(xué)家數(shù)量相同的叉子。我們將每個(gè)哲學(xué)家的左右叉子傳遞給他,嘗試使用synchronized關(guān)鍵字鎖定這些叉子。

運(yùn)行此代碼將產(chǎn)生類似以下的輸出。你的輸出很可能與下面給出的不同,主要是因?yàn)閟leep()方法被調(diào)用的時(shí)間間隔不同:

Philosopher 1 8038014601251: Thinking
Philosopher 2 8038014828862: Thinking
Philosopher 3 8038015066722: Thinking
Philosopher 4 8038015284511: Thinking
Philosopher 5 8038015468564: Thinking
Philosopher 1 8038016857288: Picked up left fork
Philosopher 1 8038022332758: Picked up right fork - eating
Philosopher 3 8038028886069: Picked up left fork
Philosopher 4 8038063952219: Picked up left fork
Philosopher 1 8038067505168: Put down right fork
Philosopher 2 8038089505264: Picked up left fork
Philosopher 1 8038089505264: Put down left fork. Back to thinking
Philosopher 5 8038111040317: Picked up left fork

所有哲學(xué)家最初都在思考,我們看到哲學(xué)家1拿起了左右叉子,然后進(jìn)食并放下兩把叉子,之后哲學(xué)家5拿起了叉子。

四、解決方案的問題:死鎖

雖然上述解決方案看起來是正確的,但存在死鎖問題。

死鎖是指系統(tǒng)中的每個(gè)進(jìn)程都在等待獲取其他進(jìn)程持有的資源,從而導(dǎo)致系統(tǒng)進(jìn)展停滯的情況。

我們可以通過多次運(yùn)行上述代碼并檢查有時(shí)代碼是否掛起來確認(rèn)這一點(diǎn)。以下是一個(gè)示例輸出,展示了上述問題:

Philosopher 1 8487540546530: Thinking
Philosopher 2 8487542012975: Thinking
Philosopher 3 8487543057508: Thinking
Philosopher 4 8487543318428: Thinking
Philosopher 5 8487544590144: Thinking
Philosopher 3 8487589069046: Picked up left fork
Philosopher 1 8487596641267: Picked up left fork
Philosopher 5 8487597646086: Picked up left fork
Philosopher 4 8487617680958: Picked up left fork
Philosopher 2 8487631148853: Picked up left fork

在這種情況下,每個(gè)哲學(xué)家都拿到了他左邊的叉子,但無法拿到右邊的叉子,因?yàn)樗泥従右呀?jīng)拿到了。這種情況通常稱為循環(huán)等待,是導(dǎo)致死鎖并阻止系統(tǒng)進(jìn)展的條件之一。

五、解決死鎖

如上文所述,死鎖的主要原因是循環(huán)等待條件,即每個(gè)進(jìn)程都在等待其他進(jìn)程持有的資源。因此,為了避免死鎖情況,我們需要確保打破循環(huán)等待條件。有幾種方法可以實(shí)現(xiàn)這一點(diǎn),最簡(jiǎn)單的方法如下:

除了一個(gè)哲學(xué)家先拿右邊的叉子外,所有哲學(xué)家都先拿左邊的叉子。

我們通過在現(xiàn)有代碼中進(jìn)行相對(duì)較小的更改來實(shí)現(xiàn)這一點(diǎn):

public class DiningPhilosophers {

    public static void main(String[] args) throws Exception {

        final Philosopher[] philosophers = new Philosopher[5];
        Object[] forks = new Object[philosophers.length];

        for (int i = 0; i < forks.length; i++) {
            forks[i] = new Object();
        }

        for (int i = 0; i < philosophers.length; i++) {
            Object leftFork = forks[i];
            Object rightFork = forks[(i + 1) % forks.length];

            if (i == philosophers.length - 1) {
                // 最后一個(gè)哲學(xué)家先拿起右邊的叉子
                philosophers[i] = new Philosopher(rightFork, leftFork);
            } else {
                philosophers[i] = new Philosopher(leftFork, rightFork);
            }

            Thread t = new Thread(philosophers[i], "Philosopher " + (i + 1));
            t.start();
        }
    }
}

我們引入了一個(gè)條件,使最后一個(gè)哲學(xué)家先拿起右邊的叉子,而不是左邊的。這打破了循環(huán)等待條件,我們可以避免死鎖。

以下輸出顯示了所有哲學(xué)家都有機(jī)會(huì)思考和進(jìn)食而不導(dǎo)致死鎖的一種情況:

Philosopher 1 88519839556188: Thinking
Philosopher 2 88519840186495: Thinking
Philosopher 3 88519840647695: Thinking
Philosopher 4 88519840870182: Thinking
Philosopher 5 88519840956443: Thinking
Philosopher 3 88519864404195: Picked up left fork
Philosopher 5 88519871990082: Picked up left fork
Philosopher 4 88519874059504: Picked up left fork
Philosopher 5 88519876989405: Picked up right fork - eating
Philosopher 2 88519935045524: Picked up left fork
Philosopher 5 88519951109805: Put down right fork
Philosopher 4 88519997119634: Picked up right fork - eating
Philosopher 5 88519997113229: Put down left fork. Back to thinking
Philosopher 5 88520011135846: Thinking
Philosopher 1 88520011129013: Picked up left fork
Philosopher 4 88520028194269: Put down right fork
Philosopher 4 88520057160194: Put down left fork. Back to thinking
Philosopher 3 88520067162257: Picked up right fork - eating
Philosopher 4 88520067158414: Thinking
Philosopher 3 88520160247801: Put down right fork
Philosopher 4 88520249049308: Picked up left fork
Philosopher 3 88520249119769: Put down left fork. Back to thinking

通過多次運(yùn)行代碼可以驗(yàn)證,系統(tǒng)不再出現(xiàn)之前的死鎖情況。

文末總結(jié)

在本文中,我們探討了著名的哲學(xué)家就餐問題以及循環(huán)等待和死鎖的概念。我們編寫了一個(gè)導(dǎo)致死鎖的簡(jiǎn)單解決方案,并進(jìn)行了簡(jiǎn)單的更改以打破循環(huán)等待并避免死鎖。這只是一個(gè)開始,實(shí)際上還有更復(fù)雜的解決方案。

責(zé)任編輯:武曉燕 來源: 看山的小屋
相關(guān)推薦

2025-01-06 09:09:07

了Java死鎖多線程

2013-08-30 09:54:18

2011-11-11 16:10:59

2013-12-31 15:25:00

2011-02-12 09:11:24

RSARSA大會(huì)2011 RSA

2011-03-29 16:55:56

高斯林Java之父高司令

2021-10-20 14:04:10

代碼注釋接口

2019-07-22 08:28:38

2012-06-07 09:00:06

2023-04-07 19:25:04

后端開發(fā)

2013-10-29 13:33:33

IDC

2020-08-18 10:33:47

智能手機(jī)相機(jī)計(jì)算

2012-04-13 10:11:58

Windows 8泄露

2013-04-06 18:52:20

2009-06-08 09:59:24

谷歌俄羅斯方塊版權(quán)

2015-10-10 10:51:25

數(shù)據(jù)本質(zhì)大數(shù)據(jù)

2020-04-09 08:47:38

Java對(duì)象線程

2022-07-29 14:22:11

AI

2013-01-11 16:13:55

庫克蘋果喬布斯

2021-03-10 08:05:10

Nginx面試并發(fā)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 欧美日韩在线一区二区 | 成人免费视频一区二区 | 国产精品国产精品 | 四季久久免费一区二区三区四区 | 91精品国产综合久久久久久漫画 | 久久爱综合 | 色狠狠桃花综合 | 国产一区二区三区视频 | 欧美 日韩 国产 成人 | 欧美视频一级 | 欧美一区二区免费电影 | 国产一级影片 | 精品国产乱码久久久久久蜜退臀 | 色婷婷在线视频 | 日韩成人在线播放 | 一级毛片视频在线 | 国产日韩欧美 | 特一级黄色毛片 | 成人精品一区二区三区中文字幕 | 亚洲成人一区二区三区 | 国产精品一区二 | 久久免费国产 | 中文字幕 亚洲一区 | 夜夜草 | 男人av在线播放 | 日日夜夜狠狠操 | 成人精品系列 | 91色视频在线观看 | 久久国产精品-国产精品 | 欧美激情a∨在线视频播放 成人免费共享视频 | 亚洲精品在线播放 | 免费亚洲婷婷 | 天天摸天天干 | 国产美女免费视频 | 伊人伊成久久人综合网站 | www.日日干 | 男女羞羞视频网站 | 日韩欧美中文 | 日本天天色| 艹逼网 | 日韩一区二区三区在线播放 |