JAVA-死锁的模拟和解决办法

一、哲学家就餐问题(死锁)

有 5 个哲学家,他们面前都有一双筷子,即左手有一根筷子,右手有一根筷子 。哲学家左手要拿到一根筷子,右手也要拿到一根筷子,在这种情况下哲学家才能吃饭。

死锁:如果每个哲学家都同时拿起左手的筷子,那么就形成了环形依赖,在这种特殊的情况下,每个人都拿着左手的筷子,都缺少右手的筷子,那么就没有人可以开始吃饭了,自然也就没有人会放下手中的筷子。这就陷入了死锁,形成了一个相互等待的情况。

package com.itheima.security.springboot.juc.deadlock;

/**
 * @Title: 模拟哲学家吃饭的问题  死锁问题
 * 在每个循环中,哲学家首先会开始思考,思考一段时间之后(这个时间长度可以是随机的),
 * 他感到饿了,就准备开始吃饭。在吃饭之前必须先拿到左手的筷子,再拿到右手的筷子,
 * 然后才开始吃饭;吃完之后,先放回右手的筷子,再放回左手的筷子;由于这是个 while 循环,
 * 所以他就会继续思考人生,开启下一个循环。
 * @Description: TODO
 * @Params:
 * @return
 * @throws
 * @Author: Vector
 * @DateTime:
 */
public class DiningPhilosophers {

    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) {
                    doAction("思考人生、宇宙、万物、灵魂...");
                    synchronized (leftChopstick) {
                        doAction("拿起左边的筷子");
                        synchronized (rightChopstick) {
                            doAction("拿起右边的筷子");
                            doAction("吃饭");
                            doAction("放下右边的筷子");
                        }
                        doAction("放下左边的筷子");
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        private void doAction(String action) throws InterruptedException {
            System.out.println(Thread.currentThread().getName() + " " + action);
            Thread.sleep((long) (Math.random() * 10));
        }
    }

    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];
            Object rightChopstick = chopsticks[(i + 1) % chopsticks.length];
            philosophers[i] = new Philosopher(rightChopstick, leftChopstick);
            new Thread(philosophers[i], "哲学家" + (i + 1) + "号").start();
        }
    }
}
package com.itheima.security.springboot.juc.deadlock;

/**
 * @Title: 模拟哲学家吃饭的问题--死锁
 *
 * @Description: TODO 解决死锁的办法:
 * 1.扩大锁的范围,如把几个待锁定的对象放到一个大对象里面,然后对这个大的对象加锁 todo 问题点:效率低
 * 2.
 * @throws
 * @Author: Vector
 * @DateTime:
 */
public 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) {
            try {
                Thread.sleep(1 + index);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (right) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(index + "号哲学家已经吃到饭了");
            }
        }
    }

    public static void main(String[] args) {
        ChopStick cs0 = new ChopStick();
        ChopStick cs1 = new ChopStick();
        ChopStick cs2 = new ChopStick();
        ChopStick cs3 = new ChopStick();
        ChopStick cs4 = new ChopStick();

        Philosohper p0 = new Philosohper("p0", 0, cs0, cs1);
        Philosohper p1 = new Philosohper("p1", 1, cs1, cs2);
        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();
    }
}

二、死锁的多种解决方案

1. 服务员检查
第一个解决方案就是引入服务员检查机制。比如我们引入一个服务员,当每次哲学家要吃饭时,他需要先询问服务员:我现在能否去拿筷子吃饭?此时,服务员先判断他拿筷子有没有发生死锁的可能,假如有的话,服务员会说:现在不允许你吃饭。这是一种解决方案

2. 领导调节
死锁检测和恢复策略,可以引入一个领导,这个领导进行定期巡视。如果他发现已经发生死锁了,就会剥夺某一个哲学家的筷子,让他放下。这样一来,由于这个人的牺牲,其他的哲学家就都可以吃饭了。这也是一种解决方案

3. 改变一个哲学家拿筷子的顺序(即混进一个左撇子)
我们还可以利用死锁避免策略,那就是从逻辑上去避免死锁的发生,比如改变其中一个哲学家拿筷子的顺序。我们可以让 4 个哲学家都先拿左边的筷子再拿右边的筷子,但是有一名哲学家与他们相反,他是先拿右边的再拿左边的,这样一来就不会出现循环等待同一边筷子的情况,也就不会发生死锁了

代码解释:

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];
        Object rightChopstick = chopsticks[(i + 1) % chopsticks.length];
        if (i == philosophers.length - 1) {
            philosophers[i] = new Philosopher(rightChopstick, leftChopstick);
        } else {
            philosophers[i] = new Philosopher(leftChopstick, rightChopstick);
        }
        new Thread(philosophers[i], "哲学家" + (i + 1) + "号").start();
    }
}


4.扩大锁的范围,如把几个待锁定的对象放到一个大对象里面,然后对这个大的对象加锁  问题点:效率低

5.效率更高混进一半的左撇子

/**
     * todo 更加高效的做法:即混进一半的左痞子。
     *
     * @throws
     * @Title: 在这里最主要的变化是,我们实例化哲学家对象的时候,传入的参数原本都是先传入左边
     * 的筷子再传入右边的,但是当我们发现他是最后一个哲学家的时候,也就是
     * if (i == philosophers.length - 1) ,在这种情况下,我们给它传入的筷子顺序恰好相反,
     * 这样一来,他拿筷子的顺序也就相反了,他会先拿起右边的筷子,再拿起左边的筷子
     * 那么这个程序运行的结果,是所有哲学家都可以正常地去进行思考和就餐了,并且不会发生死锁
     * @Description: TODO
     * @Author: Vector
     * @DateTime:
     */
    public static void main2(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];
            Object rightChopstick = chopsticks[(i + 1) % chopsticks.length];
            if (i % 2 == 0) {
                philosophers[i] = new Philosopher(rightChopstick, leftChopstick);
            } else {
                philosophers[i] = new Philosopher(leftChopstick, rightChopstick);
            }
            new Thread(philosophers[i], "哲学家" + (i + 1) + "号").start();
        }
    }
 
扩大锁的范围,如把几个待锁定的对象放到一个大对象里面,然后对这个大的对象加锁 todo 问题点:效率低

一、哲学家就餐问题(死锁)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值