Java 哲学家就餐问题

哲学家就餐问题

问题描述:

哲学家就餐问题,是并行程序中的一个经典问题,描述如下:
1、圆桌上有5位哲学家、每两位中间有一个筷子
2、每个哲学家有两件事要做
思考
吃饭(哲学家必须同时拿到两个筷子才能吃饭)
3、哲学家之间并不知道对方何时要吃饭、何时要思考,不能协商制定吃饭、思考策略
4、制定一个拿筷子的策略,使得哲学家不会因为拿筷子而出现死锁乐观锁
哲学家就餐问题

经典解法

  1. 资源分级的解法
    为资源(筷子)分配一个偏序结构,所有资源必须按照顺序访问,即拿起筷子必须按照一定的顺序。
    如:先拿编号较小的筷子,再拿编号较大的筷子,放下筷子的顺序则无所谓,这样按照筷子编号较小的优先拿则4号(编号最大)筷子没人拿,则其中一位哲学家就可以获取到2个筷子进行就餐,避免了死锁
    劣势:效率低下,实际问题中,计算机无法知道自己所需要的资源有哪一些,无法编号,且本身编号在实际使用中会先释放已拿到的才能获取待获取的,但本身资源都需要

  2. 仲裁者解决方案
    引入一个仲裁者,哲学家为了拿到筷子必须向仲裁者发送请求,仲裁者每次只服务一个哲学家,直到它拿到两个筷子为止,仲裁者必须要使用锁实现
    劣势:这种解决方案限制了并发性,也限制了在筷子允许的情况下,多个哲学家同时吃饭的可能性中断响应

  3. chandy/misra解法
    这是哲学家就餐问题的另一种解法,允许任意的用户(编号P1,…,Pn)争用任意数量的资源。与资源分级解法不同,这里编号随意

    1. 对每一个哲学家编号 对每一个竞争一个筷子资源的哲学家,新拿一个筷子,给编号较低的哲学家
    2. 每只筷子有两种状态:“干净的”或者“”脏的,初始所有筷子都是脏的
    3. 当一个哲学家要吃饭他必须拥有两个筷子,当他缺乏某只筷子的时候,发起请求
    4. 当拥有筷子的哲学家收到请求时,如果筷子是干净的,那么他继续留着使用,否则就擦干净并交出筷子
    5. 哲学家吃完的时候,筷子变脏,当有邻座请求筷子时,擦干净筷子,然后给邻座

代码实现:

以下代码是使用的第一种解法,也就是对资源的分级算法,完成本题。

public class PhilosopherDinner {
	//就餐人数
    private static final int member = 5;
    private static Random random = new Random();

    static tableware[] tablewares = new tableware[member];
    //餐具
    static class tableware{
        private int user = 0;
        private int index;
        tableware(int index){
            this.index = index;
        }

        public synchronized int getUser() {
            return user;
        }

        public synchronized void setUser(int user) {
            this.user = user;
        }

        public synchronized int getIndex(){
            return index;
        }
    }
    //哲学家
    static class Philosopher implements Runnable {
        private int NO;
        Philosopher(int NO){
            this.NO = NO;
        }
        @Override
        public void run() {
            while (true) {
                //思考
                int thinkTime = random.nextInt(10000);

                try {
                    System.out.println("哲学家" + NO + "思考: " +thinkTime/1000+ "秒" );
                    Thread.sleep(thinkTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //吃饭
                System.out.println("哲学家" + NO + " 准备吃饭");
                //需求筷子是NO坐标及坐标+1 对应数组下标
                int t1 = NO-1;
                int t2 = NO%member;
                while(true){
                    if(request(t1) && request(t2)){//获得餐具
                        int nextInt = random.nextInt(10000);
                        System.out.println("哲学家" + NO + " 正在吃饭,预计"+nextInt/1000 + "秒");
                        try {
                            Thread.sleep(nextInt);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //吃完放回筷子
                        tablewares[t1].setUser(0);
                        tablewares[t2].setUser(0);
                        System.out.println("哲学家" + NO + " 吃完饭了"+
                                " 归还" + tablewares[t1].getIndex()+
                                " 归还" + tablewares[t2].getIndex());
                        break;
                    }
                }
            }
        }
        //获取餐具
        private synchronized boolean request(int index) {
            int userIndex = tablewares[index].getUser();
            if(userIndex == NO)
                return true;
            if(userIndex == 0){
                tablewares[index].setUser(NO);
                System.out.println("哲学家"+NO+"获得餐具"+(index+1));
                return true;
            }

            return false;
        }
    }

    public static void main(String[] args) {
        //初始化
        for(int i=0;i<member;i++){
            tableware tableware = new tableware(i+1);
            tablewares[i] = tableware;

            Philosopher philosopher = new Philosopher(i+1);
            Thread thread = new Thread(philosopher);
            thread.setName("哲学家"+(i+1));
            thread.start();
        }
    }
}

代码中,要就餐的哲学家会首先拿自己编号下标的餐具,若未取到,则获取餐具返回false的if判断就不会成功,也就是说不会进行&&右边的获取,即不会获取下标+1号餐具。用这种方法,实现了资源的分级。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值