java代码实现并通过奇偶判断解决哲学家就餐导致的死锁问题

哲学家就餐问题

在这里插入图片描述
如上图,五位哲学家中间间隔放置了五只筷子,只有当哲学家的左手和右手都拿到筷子的时候才能够进餐。当某位哲学家进餐结束以后,会放下手中的筷子给其他哲学家使用。
在某些情况下,会出现所有哲学家都拿到一只筷子,无法进食的情况。并且每个哲学家都不会放弃已经在手中的筷子,这种情况就会一直持续下去,直到哲学家都饿死。这种现象放在计算机线程调度中,就是出现了死锁。

Java代码实现

很明显,每一个筷子可以被当成一个类。

package PhDinner;

public class ChopStick {
}

筷子类比较简单,可以不拥有成员变量及方法。
另外,哲学家类则可以类比为线程,因此可以让它继承线程类。

package PhDinner;

import lombok.AllArgsConstructor;

@AllArgsConstructor
public class Philosohper extends Thread{

    private ChopStick left;

    private ChopStick right;

    private int index;

    @Override
    public void run(){
        try {
            synchronized (right){ //哲学家先拿右手的筷子
                Thread.sleep(2000); //等待2s
                synchronized (left){ //再去拿左手的筷子
                //拿到筷子打印"eat"
                    System.out.println(index + "eat");
                }
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }

	// 测试代码
    public static void main(String[] args) {
        ChopStick c1 = new ChopStick();
        ChopStick c2 = new ChopStick();
        ChopStick c3 = new ChopStick();
        ChopStick c4 = new ChopStick();
        ChopStick c5 = new ChopStick();

        Philosohper p1 = new Philosohper(c1,c2,1);
        Philosohper p2 = new Philosohper(c2,c3,2);
        Philosohper p3 = new Philosohper(c3,c4,3);
        Philosohper p4 = new Philosohper(c4,c5,4);
        Philosohper p5 = new Philosohper(c5,c1,5);
        p1.start();
        p2.start();
        p3.start();
        p4.start();
        p5.start();
    }
}

上面的代码是无法执行出结果的,原因就是每个哲学家都占有了自己右手的筷子,但是左手的筷子被另外一个哲学家占有了,因此无法执行最后的打印语句。

解决方式

我们首先想这个问题的本质,在于每个哲学家都没有拿到左手的筷子,那如果有个哲学家特立独行,他每次先去拿左手的筷子,再去拿右手的筷子呢。这种情况下,假如他拿不到左手的筷子,也不会去拿右手的筷子了,那右边的哲学家就会有两只筷子了。所以就有一位哲学家可以吃饭,那他吃完以后把筷子都释放,其他哲学家也都能吃上饭了。
因此就有了下面的代码:
让某一位哲学家特立独行

    @Override
    public void run(){
        try {

            if(index==1){ //第一位哲学家特立独行
                synchronized (right){
                    Thread.sleep(2000);
                    synchronized (left){
                        System.out.println(index + "eat");
                    }
                }
            }else {
                synchronized (left){
                    Thread.sleep(2000);
                    synchronized (right){
                        System.out.println(index + "eat");
                    }
                }
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }

OK,运行代码,便会出现结果了;
但是这种方式有没有问题呢?还有没有可以优化的点呢,我们仔细思考一下。

问题与优化

在上面的代码中,我们只让一个哲学家特立独行,程序在执行的时候呢,线程也是串行完成的,加入有100万个哲学家的话,串行执行的时间耗费太大。
我们仔细看上面的图,一共有5支筷子,所以一次性可以让2个间隔的哲学家吃上饭。所以我们可以采用奇偶判断的方式:

    @Override
    public void run(){
        try {
            
            if(index%2==0){ //通过奇偶判断位置,采用不同的拿取筷子的方式解开死锁
                synchronized (right){
                    Thread.sleep(2000);
                    synchronized (left){
                        System.out.println(index + "eat");
                    }
                }
            }else {
                synchronized (left){
                    Thread.sleep(2000);
                    synchronized (right){
                        System.out.println(index + "eat");
                    }
                }
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }

至此,我们算是找到了一个解决哲学家进餐问题的较好的方法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值