哲学家就餐问题之java解决

前言

哲学家就餐问题是在计算机科学中的一个经典问题,用来演示在并行计算中多线程同步(Synchronization)时产生的问题。
有五个哲学家,他们共用一张圆桌,分别坐在五张椅子上。在圆桌上有五个碗和五支筷子,平时一个哲学家进行思考,饥饿时便试图取用其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐。进餐完毕,放下筷子又继续思考。

在这里插入图片描述

代码描述:

public class PhilosopherThread extends Thread {

    private Chopsticks left;

    private Chopsticks right;

    private int index;

    public PhilosopherThread(Chopsticks left, Chopsticks right, int index) {
        this.left = left;
        this.index = index;
        this.right = right;
    }

    @Override
    public void run() {
        synchronized (left) {
            try {
                Thread.sleep(3000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(index + "等待获取筷子RIGHT进行吃饭");
            synchronized (right) {
                System.out.println(index + "获取筷子RIGHT进行吃饭");
            }
        }



    }

    public static void main(String[] args) {
        Chopsticks chopsticks1 = new Chopsticks();
        Chopsticks chopsticks2 = new Chopsticks();
        Chopsticks chopsticks3 = new Chopsticks();
        Chopsticks chopsticks4 = new Chopsticks();
        Chopsticks chopsticks5 = new Chopsticks();

        PhilosopherThread philosopherThread1 = new PhilosopherThread(chopsticks1, chopsticks2, 1);
        PhilosopherThread philosopherThread2 = new PhilosopherThread(chopsticks2, chopsticks3, 2);
        PhilosopherThread philosopherThread3 = new PhilosopherThread(chopsticks3, chopsticks4, 3);
        PhilosopherThread philosopherThread4 = new PhilosopherThread(chopsticks4, chopsticks5, 4);
        PhilosopherThread philosopherThread5 = new PhilosopherThread(chopsticks5, chopsticks1, 5);
        philosopherThread1.start();
        philosopherThread2.start();
        philosopherThread3.start();
        philosopherThread4.start();
        philosopherThread5.start();
    }
}

通过运行代码,我们发现每一个哲学家都持有左边的筷子取去取右边的筷子,到第五个人的时候,他需要获取的右边筷子是第一个哲学家左手的筷子,最终形成死锁。

如何解决这个问题呢

1. 线程粗化,一个哲学家同时拥有左右筷子时才允许进餐。
2. 奇偶互反,奇数哲学家先取右边,偶数先取左边。
3. 最少保证,最多只有4名哲学家同时去获取左边筷子,最后一名先获取右边筷子,也就是说最少保证有一个哲学家和其他哲学家的取筷顺序是相反的。

1.线程粗化

看代码,我们得知每一个哲学家都是先持有左边的筷子,然后再去获取右边的筷子,如果我们让哲学家同时持有左右筷子的时候在进餐,是不是就能保证不会出现死锁的情况呢。

在java中,没办法同时获取两个对象的锁,必须先获取左边或者右边筷子,才能获取另外一只筷子的锁,这时候我们抽象一下,把左右筷子当成一整个变量,谁获取到了这个变量的锁,就当谁同时持有了左右两只筷子。

也就是说通过一个全局变量,使用互斥锁的方式,谁先获得了,谁就允许进餐。

这种方式可不可行呢,我们看看代码

   public class PhilosopherThread extends Thread {
    
        private Chopsticks left;
    
        private Chopsticks right;
    
        private int index;
    
        private String mutex;
    
        public PhilosopherThread(Chopsticks left, Chopsticks right, int index, String mutex) {
            this.left = left;
            this.index = index;
            this.right = right;
            this.mutex = mutex;
        }
    
        @Override
        public void run() {
            try {
                Thread.sleep(3000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (mutex) {
                System.out.println(index + " :获取筷进行吃饭:" + left + ":" + right);
    
            }
    
        }
    
        public static void main(String[] args) {
            Chopsticks chopsticks1 = new Chopsticks();
            Chopsticks chopsticks2 = new Chopsticks();
            Chopsticks chopsticks3 = new Chopsticks();
            Chopsticks chopsticks4 = new Chopsticks();
            Chopsticks chopsticks5 = new Chopsticks();
    
            String mutex="lock";
            PhilosopherThread philosopherThread1 = new PhilosopherThread(chopsticks1, chopsticks2, 1,mutex);
            PhilosopherThread philosopherThread2 = new PhilosopherThread(chopsticks2, chopsticks3, 2,mutex);
            PhilosopherThread philosopherThread3 = new PhilosopherThread(chopsticks3, chopsticks4, 3,mutex);
            PhilosopherThread philosopherThread4 = new PhilosopherThread(chopsticks4, chopsticks5, 4,mutex);
            PhilosopherThread philosopherThread5 = new PhilosopherThread(chopsticks5, chopsticks1, 5,mutex);
            philosopherThread1.start();
            philosopherThread2.start();
            philosopherThread3.start();
            philosopherThread4.start();
            philosopherThread5.start();
    
        }
    }

2.奇偶互反

奇偶互反实际上就是针对哲学家编号,奇偶数获取相反的顺序,

比如哲学家编号从1到5,奇数的先取左边筷子,偶数的取右边筷子。那么1号哲学家取左边,2号哲学家取右边,3号哲学家取左边,以此类推,通过这样编号得到的结果就是,1号获取到左右筷子,开始进餐,2号获取到右边筷子,等待1号吃完放下在获取左边筷子(1号的右边是2号的左边),3号等待获取左边筷子,4号这时候又能同时拥有左右筷子开始进餐。

  public class PhilosopherThread extends Thread {
    
        private Chopsticks left;
    
        private Chopsticks right;
    
        private int index;
    
    
        public PhilosopherThread(Chopsticks left, Chopsticks right, int index) {
            this.left = left;
            this.index = index;
            this.right = right;
        }
    
        @Override
        public void run() {
    
            if (index % 2 == 0) {
                synchronized (right) {
                    try {
                        Thread.sleep(3000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(index + "等待获取筷子LEFT进行吃饭");
                    synchronized (right) {
                        System.out.println(index + "获取筷子LEFT进行吃饭");
                    }
                }
            } else {
                synchronized (left) {
                    try {
                        Thread.sleep(3000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(index + "等待获取筷子RIGHT进行吃饭");
                    synchronized (right) {
                        System.out.println(index + "获取筷子RIGHT进行吃饭");
                    }
                }
            }
    
        }
    
        public static void main(String[] args) {
            Chopsticks chopsticks1 = new Chopsticks();
            Chopsticks chopsticks2 = new Chopsticks();
            Chopsticks chopsticks3 = new Chopsticks();
            Chopsticks chopsticks4 = new Chopsticks();
            Chopsticks chopsticks5 = new Chopsticks();
    
            PhilosopherThread philosopherThread1 = new PhilosopherThread(chopsticks1, chopsticks2, 1);
            PhilosopherThread philosopherThread2 = new PhilosopherThread(chopsticks2, chopsticks3, 2);
            PhilosopherThread philosopherThread3 = new PhilosopherThread(chopsticks3, chopsticks4, 3);
            PhilosopherThread philosopherThread4 = new PhilosopherThread(chopsticks4, chopsticks5, 4);
            PhilosopherThread philosopherThread5 = new PhilosopherThread(chopsticks5, chopsticks1, 5);
            philosopherThread1.start();
            philosopherThread2.start();
            philosopherThread3.start();
            philosopherThread4.start();
            philosopherThread5.start();
    
        }
    }

3.最少保证

最少保证,最多只有4名哲学家同时去获取左边筷子,最后一名先获取右边筷子,也就是说最少保证有一个哲学家和其他哲学家的取筷顺序是相反的。这实际上和奇偶的思路是一致的,每次保证有一个人哲学家获取筷子的顺序和其他哲学家相反,这样死锁的条件就不成立。

   public class PhilosopherThread extends Thread {
    
        private Chopsticks left;
    
        private Chopsticks right;
    
        private int index;
    
    
        public PhilosopherThread(Chopsticks left, Chopsticks right, int index) {
            this.left = left;
            this.index = index;
            this.right = right;
        }
    
        @Override
        public void run() {
    
            if (index==5) {
                synchronized (right) {
                    try {
                        Thread.sleep(3000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(index + "等待获取筷子LEFT进行吃饭");
                    synchronized (right) {
                        System.out.println(index + "获取筷子LEFT进行吃饭");
                    }
                }
            } else {
                synchronized (left) {
                    try {
                        Thread.sleep(3000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(index + "等待获取筷子RIGHT进行吃饭");
                    synchronized (right) {
                        System.out.println(index + "获取筷子RIGHT进行吃饭");
                    }
                }
            }
    
        }
    
        public static void main(String[] args) {
            Chopsticks chopsticks1 = new Chopsticks();
            Chopsticks chopsticks2 = new Chopsticks();
            Chopsticks chopsticks3 = new Chopsticks();
            Chopsticks chopsticks4 = new Chopsticks();
            Chopsticks chopsticks5 = new Chopsticks();
    
            PhilosopherThread philosopherThread1 = new PhilosopherThread(chopsticks1, chopsticks2, 1);
            PhilosopherThread philosopherThread2 = new PhilosopherThread(chopsticks2, chopsticks3, 2);
            PhilosopherThread philosopherThread3 = new PhilosopherThread(chopsticks3, chopsticks4, 3);
            PhilosopherThread philosopherThread4 = new PhilosopherThread(chopsticks4, chopsticks5, 4);
            PhilosopherThread philosopherThread5 = new PhilosopherThread(chopsticks5, chopsticks1, 5);
            philosopherThread1.start();
            philosopherThread2.start();
            philosopherThread3.start();
            philosopherThread4.start();
            philosopherThread5.start();
    
        }
  • 19
    点赞
  • 84
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
哲学家就餐问题可以使用Java语言通过线程控制来实现。 具体实现如下: 1. 定义一个Chopstick类表示筷子,其中包含一个标识唯一性的id属性。 ``` class Chopstick { private int id; public Chopstick(int id) { this.id = id; } public int getId() { return id; } } ``` 2. 定义一个Philosopher类表示哲学家,其中包含一个标识唯一性的id属性和两只筷子属性,表示他需要用到的两只筷子。 ``` class Philosopher implements Runnable { private int id; private Chopstick leftChopstick; private Chopstick rightChopstick; public Philosopher(int id, Chopstick leftChopstick, Chopstick rightChopstick) { this.id = id; this.leftChopstick = leftChopstick; this.rightChopstick = rightChopstick; } @Override public void run() { try { while (true) { // 哲学家思考 think(); // 哲学家拿起左边的筷子 synchronized (leftChopstick) { System.out.println("Philosopher " + id + " picked up " + leftChopstick.getId() + " chopstick."); // 哲学家拿起右边的筷子 synchronized (rightChopstick) { System.out.println("Philosopher " + id + " picked up " + rightChopstick.getId() + " chopstick."); // 哲学家就餐 eat(); } } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } private void think() throws InterruptedException { System.out.println("Philosopher " + id + " is thinking."); Thread.sleep(new Random().nextInt(1000)); } private void eat() throws InterruptedException { System.out.println("Philosopher " + id + " is eating."); Thread.sleep(new Random().nextInt(1000)); System.out.println("Philosopher " + id + " put down " + rightChopstick.getId() + " chopstick."); System.out.println("Philosopher " + id + " put down " + leftChopstick.getId() + " chopstick."); } } ``` 3. 在main方法中创建五个哲学家和五只筷子,并启动每个哲学家的线程。 ``` public class DiningPhilosophers { public static void main(String[] args) { Chopstick chopstick1 = new Chopstick(1); Chopstick chopstick2 = new Chopstick(2); Chopstick chopstick3 = new Chopstick(3); Chopstick chopstick4 = new Chopstick(4); Chopstick chopstick5 = new Chopstick(5); Philosopher philosopher1 = new Philosopher(1, chopstick1, chopstick2); Philosopher philosopher2 = new Philosopher(2, chopstick2, chopstick3); Philosopher philosopher3 = new Philosopher(3, chopstick3, chopstick4); Philosopher philosopher4 = new Philosopher(4, chopstick4, chopstick5); Philosopher philosopher5 = new Philosopher(5, chopstick5, chopstick1); new Thread(philosopher1).start(); new Thread(philosopher2).start(); new Thread(philosopher3).start(); new Thread(philosopher4).start(); new Thread(philosopher5).start(); } } ``` 在程序运行时,哲学家们会不断地思考、拿起筷子、就餐和放下筷子,直到程序被手动终止。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值