91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java線程同步問題實例分析

發布時間:2022-02-14 16:14:56 來源:億速云 閱讀:132 作者:iii 欄目:開發技術

這篇文章主要講解了“Java線程同步問題實例分析”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Java線程同步問題實例分析”吧!

1.場景

有五位沉默的哲學家圍坐在一張圓桌旁,他們一生都在吃東西和思考。

有五只筷子供他們使用,哲學家需要雙手拿到一雙筷子之后才能吃飯;吃完后會將筷子放下繼續思考。

那么現在有一個問題,我們需要想出一種方案,如何保證哲學家們可以交替吃飯和思考,而不會被餓死。

Java線程同步問題實例分析

上面這個問題是由Dijkstra提出的一個經典的線程同步問題。

2.解決方案

我們在開始想如何解決問題之前,可以先將這個場景通過代碼還原,在程序中進行建模。

每一只筷子可以看做是一個資源數據,都可以被它兩邊的哲學家嘗試去獲取,并且同一時間只能由其中一人持有,這可以通過我們JUC包中的信號量Semaphore來表示。

然后,每個哲學家可以看做是一個線程,每個線程中的run方法內容都是先進行思考,然后試圖獲取左右兩邊的筷子吃飯,吃完飯后繼續思考。

通過上面的分析,我們的代碼實現如下:

/**
 * @author 小黑說Java
 * @ClassName DiningPhilosophers
 * @Description 哲學家就餐問題
 * @date 2022/2/6
 **/
@Slf4j
public class DiningPhilosophers implements Runnable {

    private final int id;

    public DiningPhilosophers(int id) {
        this.id = id;
    }

    private static final Random random = new Random(System.currentTimeMillis());

    private static final Semaphore[] forks = new Semaphore[5];

    // 初始化信號量,每個信號量為1,代表1只筷子
    static {
        forks[0] = new Semaphore(1);
        forks[1] = new Semaphore(1);
        forks[2] = new Semaphore(1);
        forks[3] = new Semaphore(1);
        forks[4] = new Semaphore(1);
    }

    @Override
    public void run() {
        try {
            while (true) {
                think();
                eat(id);
            }
        } catch (InterruptedException e) {
            log.error("異常中斷", e);
        }
    }

    /**
     * 哲學家思考隨機時間
     */
    private void think() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(random.nextInt(100));
    }

    private void eat(int id) {
        // TODO
    }

}

接下來,我們思考一下,如何實現哲學家吃飯的邏輯。

當一個哲學家需要吃飯時,他要拿起左右兩邊的筷子。

所以:

  • 哲學家 A(0) 需要筷子0 和 4

  • 哲學家 B(1) 需要筷子 1 和 0

  • 哲學家 C(2) 需要筷子 2 和 1

  • 哲學家 D(3) 需要筷子 3 和 2

  • 哲學家 E(4) 需要筷子 4 和 3

所以每個哲學家線程都應該有個編號,所以我在DiningPhilosophers中定義了屬性id表示哲學家的編號。

在吃飯方法中,需要根據id來決定獲取哪只筷子。

左手邊的筷子可以有用fork[id]表示;

右手邊的筷子用fork[(id+4)%5]表示。

那么我們的eat方法的實現如下:

private void eat(int id) throws InterruptedException {
    // 先拿左邊的筷子
    forks[id].acquire();

    // 然后拿右邊的筷子
    forks[(id + 4) % 5].acquire();

    // 吃一口飯
    log.info("哲學家{}正在吃飯~", id);

    // 依次放下左邊的筷子和右邊的筷子
    forks[id].release();
    forks[(id + 4) % 5].release();
}

我們接著來測試我們的完整代碼。

/**
 * @author 小黑說Java
 * @ClassName DiningPhilosophers
 * @Description 哲學家就餐問題
 * @date 2022/2/6
 **/
@Slf4j
public class DiningPhilosophers implements Runnable {

    private final int id;

    public DiningPhilosophers(int id) {
        this.id = id;
    }

    private static final Random random = new Random(System.currentTimeMillis());

    private static final Semaphore[] forks = new Semaphore[5];

    // 初始化信號量,每個信號量為1,代表1只筷子
    static {
        forks[0] = new Semaphore(1);
        forks[1] = new Semaphore(1);
        forks[2] = new Semaphore(1);
        forks[3] = new Semaphore(1);
        forks[4] = new Semaphore(1);
    }

    @Override
    public void run() {
        try {
            while (true) {
                think();
                eat(id);
            }
        } catch (InterruptedException e) {
            log.error("異常中斷", e);
        }
    }

    /**
     * 哲學家思考隨機時間
     */
    private void think() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(random.nextInt(100));
    }

    private void eat(int id) throws InterruptedException {
        // 先拿左邊的筷子
        forks[id].acquire();

        // 然后拿右邊的筷子
        forks[(id + 4) % 5].acquire();

        // 吃一口飯
        log.info("哲學家{}正在吃飯~", id);

        // 依次放下左邊的筷子和右邊的筷子
        forks[id].release();
        forks[(id + 4) % 5].release();
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(new DiningPhilosophers(i)).start();
        }
    }
}

運行上面的代碼后,會發現程序在運行一段時間后會進入死鎖狀態。

這種情況是因為,在某一時刻,所有的哲學家都獲取到了左手邊的筷子,而無法獲取到右手邊的筷子,導致沒有人可以到東西,陷入僵局。

該如何避免出現這種死鎖問題呢?

方法一:限制吃飯的哲學家人數

很簡單的一種方法,就是在一個時間點,只能有最多4個哲學家開始吃飯。4個哲學家分5只筷子,則永遠不會發生死鎖。

要實現這種方法,我們可以再定義一個許可數為4的信號量Semaphore,表示剩余可以吃飯的哲學家名額。

代碼實現如下:

/**
 * @author 小黑說Java
 * @ClassName DiningPhilosophers
 * @Description 哲學家就餐問題
 * @date 2022/2/6
 **/
@Slf4j
public class DiningPhilosophers implements Runnable {

    private final int id;

    public DiningPhilosophers(int id) {
        this.id = id;
    }

    private static final Random random = new Random(System.currentTimeMillis());

    private static final Semaphore[] forks = new Semaphore[5];

    private static final Semaphore maxDiners = new Semaphore(4);


    // 初始化信號量,每個信號量為1,代表1只筷子
    static {
        forks[0] = new Semaphore(1);
        forks[1] = new Semaphore(1);
        forks[2] = new Semaphore(1);
        forks[3] = new Semaphore(1);
        forks[4] = new Semaphore(1);
    }

    @Override
    public void run() {
        try {
            while (true) {
                think();
                eat(id);
            }
        } catch (InterruptedException e) {
            log.error("異常中斷", e);
        }
    }

    /**
     * 哲學家思考隨機時間
     */
    private void think() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(random.nextInt(100));
    }

    private void eat(int id) throws InterruptedException {
        // 先獲得吃飯名額
        maxDiners.acquire();

        // 先拿左邊的筷子
        forks[id].acquire();

        // 然后拿右邊的筷子
        forks[(id + 4) % 5].acquire();

        // 吃一口飯
        log.info("哲學家{}正在吃飯~", id);

        // 依次放下左邊的筷子和右邊的筷子
        forks[id].release();
        forks[(id + 4) % 5].release();

        // 吃完之后歸還吃飯名額
        maxDiners.release();
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(new DiningPhilosophers(i)).start();
        }
    }
}

方法二:找到一個左撇子哲學家

這種方法是讓其中一個哲學家和其他哲學家拿筷子的順序和其他哲學家不一樣。

比如其他人都是先拿右手邊再拿左手邊,而這個左撇子哲學家則先拿左手邊再拿右手邊。

而哪一位哲學家被選為左撇子并不重要,因為桌子是圓的,所以我們就選擇0號哲學家為左撇子。

代碼實現如下:

/**
 * @ClassName DiningPhilosophers
 * @Description 哲學家就餐問題
 * @date 2022/2/6
 **/
@Slf4j
public class DiningPhilosophers implements Runnable {

    private final int id;

    public DiningPhilosophers(int id) {
        this.id = id;
    }

    private static final Random random = new Random(System.currentTimeMillis());

    private static final Semaphore[] forks = new Semaphore[5];


    // 初始化信號量,每個信號量為1,代表1只筷子
    static {
        forks[0] = new Semaphore(1);
        forks[1] = new Semaphore(1);
        forks[2] = new Semaphore(1);
        forks[3] = new Semaphore(1);
        forks[4] = new Semaphore(1);
    }

    @Override
    public void run() {
        try {
            while (true) {
                think();
                eat(id);
            }
        } catch (InterruptedException e) {
            log.error("異常中斷", e);
        }
    }

    /**
     * 哲學家思考隨機時間
     */
    private void think() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(random.nextInt(100));
    }

    private void eat(int id) throws InterruptedException {
        if (id == 0) {
            hanleLeftFirst(id);
        } else {
            hanldRightFirst(id);
        }
        // 吃一口飯
        log.info("哲學家{}正在吃飯~", id);
        forks[id].release();
        forks[(id + 4) % 5].release();
    }

    private void hanleLeftFirst(int id) throws InterruptedException {
        forks[id].acquire();
        forks[(id + 4) % 5].acquire();
    }

    private void hanldRightFirst(int id) throws InterruptedException {
        forks[(id + 4) % 5].acquire();
        forks[id].acquire();
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(new DiningPhilosophers(i)).start();
        }
    }
}

感謝各位的閱讀,以上就是“Java線程同步問題實例分析”的內容了,經過本文的學習后,相信大家對Java線程同步問題實例分析這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

许昌市| 怀柔区| 赤城县| 仙居县| 高雄县| 宁化县| 洛浦县| 临高县| 昔阳县| 平阳县| 靖远县| 富民县| 广东省| 丹巴县| 崇礼县| 张家港市| 北碚区| 华亭县| 灵台县| 高雄市| 邢台市| 晋中市| 奇台县| 屏东市| 旌德县| 五原县| 工布江达县| 舞钢市| 肇州县| 察隅县| 休宁县| 瑞丽市| 龙游县| 恩平市| 滨海县| 汉阴县| 耒阳市| 墨脱县| 金堂县| 澜沧| 平果县|