java实现哲学家进餐问题,及其死锁问题的解决

首先我们来了解一下哲学家进餐问题的背景:
话说有5个哲学家围在一张桌子上吃饭,桌上只有5g根筷子,一个要吃饭必须的得有两根筷子,哲学家要吃饭时总是先拿起左边的筷子,在拿起右边的筷子,这样最佳的情况是可同时有两人可以进餐,最坏的情况是大家都拿起了左边的筷子,大家都没得吃。哲学家吃完时会停下来思考一段时间,等饿了在吃。
下面我们来看一下java如何模拟哲学家进餐问题

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
//哲学家吃饭问题
public class ETTest {
    //创建大小为5的信号量数组,模拟5根筷子
    static Semaphore[] arry=new Semaphore[5];
    public static void main(String[] args) {
        //创建一个5个线程的线程池
        ExecutorService es=Executors.newFixedThreadPool(5);

        //初始化信号量
        for(int i=0;i<5;i++){
            arry[i]=new Semaphore(1,true);
        }
        //创建5个哲学家 但这样有可能会产生死锁问题
        for(int i=0;i<5;i++){
            es.execute(new ActionRunnable(i));
        }

    }
    //第i+1号哲学家的活动过程
    static class ActionRunnable implements Runnable{
        private int i=0;
        ActionRunnable(int i){
            this.i=i;
        }

        @Override
        public void run() {
            while(!Thread.interrupted()){
                try {
                    arry[i].acquire();
                    //请求右边的筷子
                    arry[(i+1)%5].acquire();
                    //吃饭
                    System.out.println("我是哲学家"+(i+1)+"号我在吃饭");
                    //释放左手的筷子
                    arry[i].release();
                    //释放右手的筷子
                    arry[(i+1)%5].release();
                     //哲学家开始思考
                    System.out.println("我是哲学家"+(i+1)+"号我吃饱了我要开始思考了");
                    //通知cpu 将调度权让给其他哲学家线程
                    Thread.yield();
                    //思考1秒
                    //把休眠关闭,造成死锁的概率就会增加 
                    //Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }


            }

        }

    }

}

我们在让哲学家吃完后,不进行思考,立即进入下一轮的抢筷子竞争中,以增加死锁出现的概率。
运行上面代码后我们会发现程序死锁了
这样的死锁问题该如何解决了。
有以下俩种解决策略
第一种:
让最多只有四个哲学家可以同时拿起左边的筷子,这样我们就可以保证,最差情况都有至少有一个哲学家可以进餐成功,进餐成功后就会释放筷子资源,这样就不会造成死锁啦。
那java代码是如实现的了。
看下面我基于上面的代码做出的修改。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
//哲学家吃饭问题
public class ETTest2 {
    //创建大小为5的信号量数组,模拟5根筷子
    static Semaphore[] arry=new Semaphore[5];
    //定义一个值为4的信号量,代表最多只能有四个哲学家拿起左边的筷子
      static  Semaphore leftCount=new Semaphore(4,true);
    public static void main(String[] args) {
        //创建一个5个线程的线程池
        ExecutorService es=Executors.newFixedThreadPool(5);

        //初始化信号量
        for(int i=0;i<5;i++){
            arry[i]=new Semaphore(1,true);
        }
        //创建5个哲学家 但这样有可能会产生死锁问题
        for(int i=0;i<5;i++){
            es.execute(new ActionRunnable(i));
        }

    }
    //第i+1号哲学家的活动过程
    static class ActionRunnable implements Runnable{
        private int i=0;
        ActionRunnable(int i){
            this.i=i;
        }

        @Override
        public void run() {
            while(!Thread.interrupted()){
                try {
                    //看拿起左边筷子的线程数是否已满,可以,则能拿起左边筷子的线程数减一,不能则等待
                    leftCount.acquire();
                    arry[i].acquire();
                    //请求右边的筷子
                    arry[(i+1)%5].acquire();
                    //吃饭
                    System.out.println("我是哲学家"+(i+1)+"号我在吃饭");
                    //释放左手的筷子
                    arry[i].release();
                    //能拿起左边筷子的线程数量加一
                    leftCount.release();
                    //释放右手的筷子
                    arry[(i+1)%5].release();
                     //哲学家开始思考
                    System.out.println("我是哲学家"+(i+1)+"号我吃饱了我要开始思考了");
                    //通知cpu 将调度权让给其他哲学家线程
                    Thread.yield();
                    //思考1秒
                    //把休眠关闭,造成死锁的概率就会增加 
                    //Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }


            }

        }

    }

}

第二种策略为,奇数号的哲学家先拿起左边的筷子,在拿起右边的筷子。偶数号的哲学家先拿起右边的筷子,再拿起左边的筷子,则以就变成,只有1号和2号哲学家会同时竞争1号的筷子,3号和4四号的哲学家会同时竞争3号的筷子,即5位哲学家会先竞争奇数号的筷子,再去竞争偶数号的筷子,最后总会有一个哲学家可以进餐成功。
下面来看一下代码是如何实现的

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
//哲学家进餐问题
public class ETTest {
    //创建大小为5的信号量数组,模拟5根筷子
    static Semaphore[] arry=new Semaphore[5];
    public static void main(String[] args) {
        //创建一个5个线程的线程池
        ExecutorService es=Executors.newFixedThreadPool(5);

        //初始化信号量
        for(int i=0;i<5;i++){
            arry[i]=new Semaphore(1,true);
        }
        //创建5个哲学家 但这样有可能会产生死锁问题
        for(int i=0;i<5;i++){
            es.execute(new ActionRunnable(i));
        }

    }
    //第i+1号哲学家的活动过程
    static class ActionRunnable implements Runnable{
        private int i=0;
        ActionRunnable(int i){
            this.i=i;
        }

        @Override
        public void run() {
            while(!Thread.interrupted()){
                try {
                    if((i+1)%2!=0){
                    //奇数号哲学家
                    //请求左边的筷子
                    arry[i].acquire();
                    //请求右边的筷子
                    arry[(i+1)%5].acquire();
                    }else{
                    //偶数号哲学家
                        //请求右边的筷子
                        arry[(i+1)%5].acquire();
                        //再请求左边的筷子
                        arry[i].acquire();
                    }
                    //吃饭
                    System.out.println("我是哲学家"+(i+1)+"号我在吃饭");

                    if((i+1)%2!=0){
                    //奇数号哲学家
                    //释放左手的筷子
                    arry[i].release();
                    //释放右手的筷子
                    arry[(i+1)%5].release();
                    }else{
                    //偶数号的哲学家
                        arry[(i+1)%5].release();
                        arry[i].release();
                    }
                     //哲学家开始思考
                    System.out.println("我是哲学家"+(i+1)+"号我吃饱了我要开始思考了");
                    //通知cpu 将调度权让给其他哲学家线程
                    Thread.yield();
                    //思考1秒
                    //把休眠关闭,造成死锁的概率就会增加 
                    //Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }


            }

        }

    }

}

好了博客写完了,你有没有学到点东西了。

    原文作者:java锁
    原文地址: https://blog.csdn.net/gao23191879/article/details/75168867
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞