编程语言
首页 > 编程语言> > 多线程面试题——哲学家就餐问题(Java)

多线程面试题——哲学家就餐问题(Java)

作者:互联网

哲学家就餐问题

公众号:小成同学在coding
文章如有问题欢迎指正

5名哲学家,5根筷子,哲学家左右两边的筷子跟身边的人共享,只有同时拿起左手的筷子和右手的筷子,哲学家才可以夹菜。

这个问题其实是一个死锁问题。

image-20211015145428089

当0号拿着a筷子的时候,它需要申请b这根筷子,才可以夹菜,但b这根筷子在被1号使用着,因此0号无法夹菜,这时候怎么办,需要等1号吃完,把b筷子放手,而1号想夹菜则需要申请c这根筷子,但c这根筷子又被2号使用着,所以每名哲学家左手都拿着筷子,而右手的筷子都在被使用中,形成了一个死锁环

如图:

image-20211222210919265

思考一下我们这道题都需要哪些类呢?

每一个哲学家应该是一个线程

  • 模拟哲学家问题 OOA - OOD - DDD
  • class : 哲学家 class : 筷子
  • 筷子:编号
  • 哲学家:左手的筷子 右手的筷子 编号

代码实现

package com.mashibing.juc.c_33_TheDinningPhilosophersProblem;

import com.mashibing.util.SleepHelper;

public class T01_DeadLock {
    public static void main(String[] args) {
        
        // 构建过程
        // 1.创建5根筷子
        ChopStick cs0 = new ChopStick();
        ChopStick cs1 = new ChopStick();
        ChopStick cs2 = new ChopStick();
        ChopStick cs3 = new ChopStick();
        ChopStick cs4 = new ChopStick();

        // 2.创建5个线程(哲学家)
        Philosohper p0 = new Philosohper("p0", 0, cs0, cs1);// 将cs0给第0号的左手,将cs1给第0号的右手
        Philosohper p1 = new Philosohper("p1", 1, cs1, cs2);// 将cs1也给第1号的左手,cs2给第1号的右手
        Philosohper p2 = new Philosohper("p2", 2, cs2, cs3);// ...
        Philosohper p3 = new Philosohper("p3", 3, cs3, cs4);
        Philosohper p4 = new Philosohper("p4", 4, cs4, cs0);

        p0.start();
        p1.start();
        p2.start();
        p3.start();
        p4.start();
    }

    // 哲学家就是一个线程,我们定义类的时候直接继承Thread类
    public static class Philosohper extends Thread {

        private ChopStick left, right;
        private int index;

        public Philosohper(String name, int index, ChopStick left, ChopStick right) {
            this.setName(name);
            this.index = index;
            this.left = left;
            this.right = right;
        }

        @Override
        public void run() {
            // 将左手的筷子上锁
            synchronized (left) {
                SleepHelper.sleepSeconds(1 + index);
                // 抢右手的筷子(锁定A再抢B)
                synchronized (right) {
                    SleepHelper.sleepSeconds(1);
                    System.out.println(index + " 号 哲学家已经吃完");
                }
            }
        }
    }
}

但是我们执行后,程序没有做任何的输出,说明死锁已经形成了。

image-20211222214721246

死锁一般具有2把以上的锁,在锁定1把的时候等待另外1把锁。

效率不高的解法

最优解法

哲学家就餐问题解决方案:

  • 两把锁合并一把锁(5把, 5把锁合成一把锁,筷子集合,锁定整个对象)
  • 混进一个左撇子(保证有一个人可以先吃到)
  • 效率更高的写法,奇数 偶数分开,混进一半的左撇子

左撇子代码实现

public void run() {
            // SleepHelper.sleepSeconds(new Random().nextInt(5));
            if (index == 0) { // 左撇子算法 也可以index % 2 == 0
				// index为0时,先锁左面再锁右面
                synchronized (left) {
                    SleepHelper.sleepSeconds(1);
                    synchronized (right) {
                        SleepHelper.sleepSeconds(1);
                        System.out.println(index + " 吃完了!");
                    }
                }
            } else {
                // index不为0时,先锁右面再锁左面
                synchronized (right) {
                    SleepHelper.sleepSeconds(1);
                    synchronized (left) {
                        SleepHelper.sleepSeconds(1);
                        System.out.println(index + " 吃完了!");
                    }
                }
            }
        }

image-20211223175417036

我们可以看到,所有人都吃完了,程序不会再产生死锁了。

标签:index,面试题,Java,哲学家,ChopStick,筷子,new,多线程,Philosohper
来源: https://blog.csdn.net/weixin_53407527/article/details/122213349