通过锁对象解决哲学家就餐问题

旧时模样 提交于 2020-01-10 12:59:55

问题描述


  • 哲学家进餐问题:5个哲学家共用一张圆桌,分别坐在周围的5张椅子上,在圆桌上有5个碗和5只筷子(注意是5只筷子,不是5双),碗和筷子交替排列。他们的生活方式是交替地进行思考(thinking)和进餐(eating)。平时,一个哲学家进行思考,饥饿时便试图取用其左右最靠近他的两只筷子,规定他必须先取左边的筷子,再取右边的筷子。只有在他拿到两只筷子时才能进餐。进餐完毕,放下筷子继续进行思考。假如5位哲学家同时饥饿,各自拿起左边的筷子时,再去拿各自右边的筷子,因为无筷子可拿而陷入无期限等待(死锁)。一种解决的办法是:至多只允许有4位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家能够拿到两只筷子,从而进餐。进餐完毕释放他用过的两只筷子,从而使更多的哲学家能够进餐。使用Java的多线程同步技术,实现上述解决方案。

主方法


package cn.learn.thread.ThreadState.practice;
//测试
public class Test {
    public static void main(String[] args) {

        //五根筷子对象
        Chopsticks[] chopsticks = new Chopsticks[5];
        for (int i = 0; i <5 ; i++) {
            chopsticks[i] = new Chopsticks("---筷子"+i);
        }

        //创建五位哲学家进程,并传递其左右两边筷子
        for (int i = 0; i < 5; i++) {
            Philosopher philosopher = new Philosopher("哲学家"+(i + 1)+": ",chopsticks[(i+1)%5],chopsticks[i]);
            philosopher.start();
        }

    }
}

筷子类


package cn.learn.thread.ThreadState.practice;

/*

  共享资源:筷子

 */
public class Chopsticks {

    private String chopsticksName;

    public Chopsticks(String chopsticksName) {
        this.chopsticksName = chopsticksName;
    }

    public String getChopsticksName() {
        return chopsticksName;
    }

    public void setChopsticksName(String chopsticksName) {
        this.chopsticksName = chopsticksName;
    }
}

哲学家进程


package cn.learn.thread.ThreadState.practice;

import java.util.Random;

public class Philosopher extends Thread {


    //左筷子
    private Chopsticks leftChopsticks;
    //右筷子
    private Chopsticks rightChopsticks;
    //静态引用类型保证同步时为同一把锁
    private static Object obj = new Object();
    private static int flag = 5;


    public Philosopher(String name, Chopsticks leftChopsticks, Chopsticks rightChopsticks) {
        //调用父方法给进程取名字
        super(name);
        this.leftChopsticks = leftChopsticks;
        this.rightChopsticks = rightChopsticks;
    }


    @Override
    public void run() {
        while (true) {
            thinking();
            takeChopsticks();
        }
    }

    //思考
    private void thinking() {
        //获取当前进程名称
        String threadName = Thread.currentThread().getName();
        Random random = new Random();
        //睡眠,不释放锁
        try {
            sleep(random.nextInt(10000)+1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(threadName + "思考中。。。");
    }

    //拿筷子
    public void takeChopsticks() {
        //让前四个哲学家先拿筷子
        synchronized (obj) {
            //如果只剩一个哲学家让他等待
            if (flag == 1) {
                try {
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //思考人数--
            flag--;
        }
        //进程进来先给左筷子请求进入并上锁,再请求右筷子
        synchronized (leftChopsticks) {
            //获取当前进程名称
            String threadName = Thread.currentThread().getName();
            //获取当前进程筷子
            String chopsticksName = leftChopsticks.getChopsticksName();
            //哲学家拿左筷子
            System.out.println(threadName + "拿起一根左筷子" + chopsticksName);


            synchronized (rightChopsticks) {
                //获取当前进程筷子
                chopsticksName = rightChopsticks.getChopsticksName();
                System.out.println(threadName + "拿起一根右筷子" + chopsticksName + "开始吃饭!");
            }
            //方法结束释放锁对象筷子

        }
        //唤醒没有拿到筷子的哲学家
        synchronized (obj) {
            obj.notify();
            flag++;
        }
    }
}

代码总结


  • 五个哲学家在争抢筷子时,由系统调度,从而可能发生死锁,通过设置flag来解决使得有一名哲学家不能进餐,当有一个人进餐完成后,flag++,思考的人员增加;通过随机设置思考睡眠时间,可以达到五个哲学家可以随机有两个人同时吃饭。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!