Java多线程 哲学家就餐死锁问题代码演示

哲学家就餐问题

哲学家就餐问题 如下图所示. 哲学家就餐的餐位上, 左手边和右手边分别只有一只筷子.
如果要就餐, 就必须拿到左手边和右手边的筷子.
流程如下:
先拿起左手的筷子
再拿起右手的筷子
如果筷子被人使用了, 那就等别人用完
吃完后, 把筷子放回原位

哲学家就餐流程的伪代码如下:

就餐问题的死锁风险 :
如果某个时刻, 每个哲学家都拿着左手边的筷子, 那么就会永远等待右手边的筷子,

哲学家就餐问题代码演示

如下为哲学家就餐问题的代码演示. 定义了Philosopher 的哲学家内部类. 实现了Runnable接口, 重写里面的run方法, 执行上面提到的哲学家每天做的事情.

  1. 思考
  2. 拿起左边筷子 (拿到左边筷子的锁)
  3. 拿起右边筷子(拿到右边筷子的锁) --> 吃饭
  4. 放下右边筷子 (释放右边筷子的锁)
  5. 放下左边筷子 (释放左边筷子的锁 )

并且定义了哲学家对象的构造方法, 传入左边筷子与右边筷子 对象.

在main 方法中, 初始化了筷子. 初始化了哲学家. 新建线程中传入哲学家对象 ,并且启动线程.

package com.thread.deadlock;

/**
 * 类名称:DiningPhilosophers
 * 类描述:  演示哲学家就餐问题导致的死锁
 *
 * @author: https://javaweixin6.blog.csdn.net/
 * 创建时间:2020/9/12 15:50
 * Version 1.0
 */
public class DiningPhilosophers {
    
    

    public static void main(String[] args) {
    
    
        //建立五个哲学家的数组
        Philosopher[] philosophers = new Philosopher[5];
        //定义筷子的个数, 为哲学家的数量
        Object[] chopsticks = new Object[philosophers.length];

        //初始化筷子
        for (int i = 0; i < chopsticks.length; i++) {
    
    
            chopsticks[i] = new Object();
        }

        //初始化哲学家
        for (int i = 0; i < philosophers.length; i++) {
    
    
            //获取左边筷子 与右边筷子
            Object leftChopstick = chopsticks[i];

            //右边的筷子为对数组长度的取余运算  避免数组越界 5%5=0  3%5=3
            Object rightChopstick = chopsticks[(i+1)%chopsticks.length ];

            //初始化哲学家
            philosophers[i] = new Philosopher(leftChopstick, rightChopstick);

            //创建并启动线程, 给线程命名
            new Thread(philosophers[i],"哲学家"+ (i+1) +"号" ).start();

        }


    }


    /**
     * 哲学家的内部类
     */
    public static class Philosopher implements Runnable {
    
    
        private Object leftChopstick;
        private Object rightChopstick;

        //初始化的时候, 传入两个对象  左边筷子 与右边筷子
        public Philosopher(Object leftChopstick, Object rightChopstick) {
    
    
            this.leftChopstick = leftChopstick;
            this.rightChopstick = rightChopstick;
        }

        @Override
        public void run() {
    
    
            try {
    
    
                //哲学家执行的任务
                while (true) {
    
    
                    //思考
                    doPrintAndWait("Thinking");

                    //拿起左边筷子
                    synchronized (leftChopstick) {
    
    
                        doPrintAndWait("Picked up left chopstick");

                        //拿起右边筷子  //吃饭
                        synchronized (rightChopstick) {
    
    
                            doPrintAndWait("Picked up right chopstick - eating");

                            //放下右边筷子, 释放锁
                            doPrintAndWait("Put down right chopstick");
                        }
                        //放下左边的筷子, 释放锁
                        doPrintAndWait("Put down left chopstick");

                    }


                }

            } catch (Exception e) {
    
    
                e.printStackTrace();

            }
        }
        //打印和执行等待的方法
        private void doPrintAndWait(String action) throws InterruptedException {
    
    
            //打印出线程的名称 和 行为
            System.out.println(Thread.currentThread().getName() + "  " + action);

            //休眠随机的时间
            Thread.sleep((long) (Math.random() * 10));

        }
    }
}

运行程序, 可以看到某一次的打印结果如下 .

五个哲学家思考, 思考完了之后, 五个哲学家都拿着左边的筷子, 导致程序死锁, 无法拿到右边的筷子 ,无法继续执行下去 .

再次多次运行程序, 可以看到如下的情况. 此时虽然程序打印了一段时间, 看似没有死锁, 但是过了一会之后, 还是会发生了死锁.

如下图所示, 发生死锁的时候, 一定是所有的哲学家此时都是拿着左边的筷子的.

猜你喜欢

转载自blog.csdn.net/qq_33229669/article/details/108541931