Java哲学家进餐问题|多线程

白昼怎懂夜的黑 提交于 2020-01-09 16:53:48

Java实验三 多线程

哲学家进餐问题:

5个哲学家共用一张圆桌,分别坐在周围的5张椅子上,

在圆桌上有5个碗和5只筷子(注意是5只筷子,不是5双),

碗和筷子交替排列。他们的生活方式是交替地进行思考(thinking)和进餐(eating)。

平时,一个哲学家进行思考,饥饿时便试图取用其左右最靠近他的两只筷子,规定他必须先取左边的筷子,再取右边的筷子。
只有在他拿到两只筷子时才能进餐。
进餐完毕,放下筷子继续进行思考。

假如5位哲学家同时饥饿,各自拿起左边的筷子时,再去拿各自右边的筷子,因为无筷子可拿而陷入无期限等待(死锁)。

一种解决的办法是:至多只允许有4位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家能够拿到两只筷子,从而进餐。

进餐完毕释放他用过的两只筷子,从而使更多的哲学家能够进餐。使用Java的多线程同步技术,实现上述解决方案。

思路都在代码里了
代码出错了
代码已改正,主要就是synchronized定义同步代码块,Num的值设置条件使不发生死锁
代码被检查了,Num也是全局变量,有可能在多线程调度时,当前线程执行num++前,另一个线程也进入了if(num<4)的代码块,理论上可能出了某些问题,仅供参考
但我感觉是没有问题的,synchronized会使多线程代码串行执行嘛,进入synchronized(left)后才给num+1,既然是串行,那么应该是等synchronized(left){串行代码块}里的代码块全部执行完才安排下一个线程吧。
实际上 运行也是正常的,不会出现5个人同时拿了左手边筷子的死锁情况。,但是小概率理论上出错的,这就是线程安全问题吧

新想法:对Num变量的操作进行加锁,是不是能够解决这个共享变量Num的线程安全问题呢??感觉可以,又感觉也不太行啊。。

synchronized (lock) {
    Num++;
}

synchronized关键字易混淆处:
用法:synchronized(一个对象){同步代码块}
这里synchronized锁住的不是这"一个对象",而是用这"一个对象"来当看门人,只允许一个线程来进入synchronized锁住的同步{代码块},所以这个代码块被称作“同步代码块”,当代码块执行完后,会让这“一个对象”(看门人),吼一声让其它线程进来。原理是:每一个java对象都关联了一个“ monitor lock”,当一个线程获取了 monitor lock 后,其它线程如果运行到获取同一个 monitor 的时候就会被 block 住。当这个线程执行完同步代码,则会释放 monitor lock。

原来的

class phiPerson implements Runnable{ //实现Runnable接口
    
    //全部使用static关键字 静态成员属于整个类,当系统第一次使用该类时,就会为其分配内存空间直到该类被卸载才会进行资源回收
    //为什么用static修饰 因为多线程多个哲学家需要共享筷子这个对象
    static Object[] chops;//static关键字修饰的变量 该类所有的对象共享同一个成员 
    static int Num = 0; //同时拿左手边筷子的人数 也是全局变量
    private int pos; //当前哲学家的编号 私有变量
    
    public phiPerson(int position,Object[] chops) { //构造函数
        // TODO Auto-generated constructor stub
        this.chops =  chops;
        this.pos = position;
    }

    @Override
    public void run() { //重写run方法
        // TODO Auto-generated method stub
        while(true) {
            int right = (pos+1)%5; //我右边筷子在数组中的下标
            int left = (pos)%5;//左边筷子在数组中的下标
            if(Num < 4) { //最多允许4个人同时拿左手边的筷子
                    synchronized (chops[left]) { //锁 左手边的筷子 就是等待左边的人用我左手边的筷子吃完了后我再拿来吃。。
                                               Num++;//同时拿左手边筷子的人的数量+1
                        System.out.println(Num);
                        System.out.println("第"+(pos+1)+"个哲学家拿了左手边的筷子");
                        synchronized(chops[right]) {//右手边的筷子 锁  就是等待右手边的筷子没人拿了我再拿
                            System.out.println("第"+(pos+1)+"个哲学家拿了右手边的筷子");
                            System.out.println("第"+(pos+1)+"个哲学家正在eating");
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                            System.out.println("第"+(pos+1)+"个哲学家吃完了,把筷子放回了原处,开始thinking");
                            Num--;//同时拿左手边筷子的人的数量-1
                        }
                    }
                
            }
        }
    }
}

public class phiEat {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Object [] chObject = new Object[5];
        for(int i=0;i<5;i++) chObject[i] = i; //object必须要初始化一下呀
        
        //5个哲学家
        phiPerson firThread = new phiPerson(0,chObject);
        phiPerson secThread = new phiPerson(1,chObject);
        phiPerson thirThread = new phiPerson(2,chObject);
        phiPerson fourThread = new phiPerson(3,chObject);
        phiPerson fifThread = new phiPerson(4,chObject);
        //开吃了 
        new Thread(firThread).start();
        new Thread(secThread).start();
        new Thread(thirThread).start();
        new Thread(fourThread).start();
        new Thread(fifThread).start();
    }

}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!