哲学家就餐问题

503 阅读3分钟

哲学家就餐问题:有五个哲学家,他们的生活方式是交替地进行思考和进餐。他们共用一张圆桌,分别坐在五张椅子上。在圆桌上有五个碗和五支筷子,平时一个哲学家进行思考,饥饿时便试图取用其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐。进餐完毕,放下筷子又继续思考。

场景:

1.1.png 因为每一个哲学家都只有吃饭或思考两种状态,只有同时拿起一双筷子时才能吃饭。我们把每一根筷子都看做是资源,两边的哲学家都看做是争抢资源的线程。

实体类筷子的代码如下

public class Chopstick {

    private String name;

    public Chopstick(String name) {
        this.name = name;
    }

    public Chopstick() {
    }

    public String getName(){
        return name;
    }

    public void setName(){
        this.name = name;
    }

    @Override
    public String toString() {
        return "Chopstick{" +
                "name='" + name + ''' +
                '}';
    }
}

哲学家的代码实现如下

/**
 * @author 鞋头冠军
 * @ClassName Philosopher
 * @Date 2023/1/12
 */
public class Philosopher extends Thread{

    private String name;
    private Chopstick right;
    private Chopstick left;

    public Philosopher(String name, Chopstick right, Chopstick left){
        this.name = name;
        this.left = left;
        this.right = right;
    }


    
    @Override
    public void run() {
        synchronized (left){
            System.out.println(this.name+"拿起左手的"+this.left);
            synchronized (right){
                System.out.println(this.name+"拿起右手的"+this.right);
                this.eat();
            }
        }
    }


    private void eat(){
        System.out.println("吃饭中...");
        long time = 1000;
        try{
            Thread.sleep(time);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println(this.name+"吃完了");
    }

}

测试类

public class Test {

    public static void main(String[] args) {

        Chopstick chopstick01 = new Chopstick("1号筷子");
        Chopstick chopstick02 = new Chopstick("2号筷子");
        Chopstick chopstick03 = new Chopstick("3号筷子");
        Chopstick chopstick04 = new Chopstick("4号筷子");
        Chopstick chopstick05 = new Chopstick("5号筷子");

        Philosopher philosopher01 = new Philosopher("1号哲学家",chopstick05,chopstick01);
        Philosopher philosopher02 = new Philosopher("2号哲学家",chopstick01,chopstick02);
        Philosopher philosopher03 = new Philosopher("3号哲学家",chopstick02,chopstick03);
        Philosopher philosopher04 = new Philosopher("4号哲学家",chopstick03,chopstick04);
        Philosopher philosopher05 = new Philosopher("5号哲学家",chopstick04,chopstick05);

        philosopher01.start();
        philosopher02.start();
        philosopher03.start();
        philosopher04.start();
        philosopher05.start();

    }
}

测试结果1:当哲学家都在同一时刻拿起了自己左手边的筷子,则直接陷入死锁的状态

1.2.png

测试结果2:因为哲学家(线程)拿起筷子的速度不同,有的哲学家拿起速度快得到了一双筷子吃到了食物,吃完食物后放下。

1.3.png

解决方案:

当哲学家要吃饭的时候,要么同时拿起一双筷子,要么一只都不拿。

思路:我们可以通过创建一个boolean数组,对不同的筷子进行状态的赋值,当哲学家拿起某根筷子时,该筷子状态改变并不能被其他的哲学家拿起。当哲学家放下筷子时,通知所有的哲学家,并改变筷子为原来的状态。 通过分析代码实现如下:

Chopstick类

public class Chopstick {


    private int name;

    public Chopstick(int name) {
        this.name = name;
    }

    public Chopstick() {
    }

    public int getName() {
        return name;
    }

    public void setName(int name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Chopstick{" +
                "name=" + name +
                '}';
    }
}

哲学家Philosopher类

/**
 * @author 鞋头冠军
 * @ClassName Philosopher
 * @Date 2023/1/12
 */
public class Philosopher extends Thread{

    private boolean[] take = new boolean[] {false, false, false, false, false};
    private static String LOCK = "lock";

    private String name;
    private Chopstick chopstick;

    public Philosopher(String name, Chopstick chopstick){
        this.name = name;
        this.chopstick = chopstick;
    }

    //拿起筷子被占用时就要放弃拿起,并释放锁
    public void takeChopstick(){
        synchronized (LOCK){
            if (take[chopstick.getName()] || take[(chopstick.getName() + 1) % 5] ){
                try{
                    LOCK.wait();
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }

            take[chopstick.getName()] = true;
            take[(chopstick.getName() + 1) % 5] = true;
        }
    }


    //放下筷子的时候也要申请锁,并通知所有线程
    public void putDownChopsticks() {
        synchronized(LOCK) {
            take[chopstick.getName()] = false;
            take[(chopstick.getName() + 1) % 5] = false;
            System.out.println(this.name + "号哲学家放下筷子"+chopstick);
            LOCK.notifyAll();
        }
    }



    @Override
    public void run(){
        while (true) {
            takeChopstick();
            eat();
            putDownChopsticks();
        }
    }



    private void eat(){
        System.out.println("吃饭中...");
        long time = 1000;
        try{
            Thread.sleep(time);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println(this.name+"吃完了");
    }

}

测试类Test

public class Test {

    public static void main(String[] args) {

        Chopstick chopstick01 = new Chopstick(1);
        Chopstick chopstick02 = new Chopstick(2);
        Chopstick chopstick03 = new Chopstick(3);
        Chopstick chopstick04 = new Chopstick(4);
        Chopstick chopstick05 = new Chopstick(5);

        Philosopher philosopher01 = new Philosopher("1号哲学家",chopstick01);
        Philosopher philosopher02 = new Philosopher("2号哲学家",chopstick02);
        Philosopher philosopher03 = new Philosopher("3号哲学家",chopstick03);
        Philosopher philosopher04 = new Philosopher("4号哲学家",chopstick04);
        Philosopher philosopher05 = new Philosopher("5号哲学家",chopstick05);

        philosopher01.start();
        philosopher02.start();
        philosopher03.start();
        philosopher04.start();
        philosopher05.start();

    }
}

测试结果:

1.4.png

小结:

对于哲学家就餐问题除了上述还有多种解决方案,欢迎在评论区留言交流。如果本文对你有所帮助,可以给博主点个赞❤。