哲学家就餐问题

Title 哲学家就餐问题是在1965年由Dijkstra提出并解决的一个问题,后来成为同步问题的一个经典问题。
Disciber 有五位哲学家围坐在一张桌子前, 他们面前都放了一碗面,彼此间放了一把叉子。因为面非常的滑,以至于使用一个叉子吃不了,所以每位哲学家需要拿起放在他左右两边的叉子吃面。同时,每位哲学家具有两种状态:进餐、思考问题。每当一位哲学家感到饥饿时,他会试图去获得位于他左右两边的那两把叉子,每次都只能拿一把,先后顺序无所谓,两把叉子都拿到后才能开始进餐,吃完了以后,他需要把两把叉子返回原处,然后继续思考。
Question 五个哲学家,五碗面和五个叉子,每碗面需要两个叉子,如何进行操作?
Analysis 1、 这里每位哲学家都具有各自的面,而相互之间竞争的是吃面的叉子,也就是说叉子是共享资源。但他们竞争的叉子也是有限制,也就是当前哲学家左右两边的叉子,而不能是其他叉子。如果进行抽象建模,可以得出如下模型:
循环:
    思考问题;
    尝试拿左边叉子;
    尝试拿右边叉子;
    吃面;
    放下左边叉子;
    放下右边的叉子;
所以我们可以采用缓冲区的方式,对叉子的使用进行控制,有权拿叉子的哲学家则分配,无权拿叉子的哲学家则需要阻塞。
Analysis 2、这里的每位哲学家都具有各自的面,而他们的状态是吃面、思考,那么每次都只能让其中的两位吃面,其他的要么在思考,要么在等待。当某个哲学家在吃面时,哪些哲学家可以吃面,哪些要等待。其实就是当前正在吃面的哲学家的左右两边的哲学家不能吃面,要么思考,要么等待,当哲学家吃完,则需要告诉左右两边还在等待的哲学家他们可以申请吃面的权利了。
循环:
    思考问题;
    申请权利;
    吃面;
    放弃权利;
    通知;

这里我们采用第二套分析方案:

import java.io.File;
import java.io.FileWriter;

public class Philosopher  extends Thread {
    private int state; //表示哲学家的状态,0思考、1表示饥饿和2表示正在吃饭
    private Philosopher left; //左边的哲学家
    private Philosopher right; //右边的哲学家
    private String name; //记录自己的名字

    public Philosopher(final String name) {
        this.name = name; 
    }

    public int getPState() {
        return this.state;
    }

    public void setPState(final int state) {
        this.state = state;
    }

    public void setLeft(final Philosopher philosopher) {
        this.left = philosopher;
    }

    public Philosopher getLeft(){
       return this.left;    
    }

    public void setRight(final Philosopher philosopher) {
        this.right = philosopher;
    }

    public Philosopher getRight(){
       return this.right;   
    }

    /**
     * 判断当前是否具备吃的条件.
     */
    public boolean canEat() {
        //当前哲学家处于饥饿状态,而且左右两边的哲学家还没有一个人处于吃的状态,那么当前哲学家就可以吃
        if(this.getPState() == 1 && this.getLeft().getPState() != 2 
            && this.getRight().getPState() != 2) { 
                return true; 
        } 
        return false;
    }

    /**
     * 哲学家吃饭....
     */
    public synchronized void eating() {
    /*ERROR: Exception Current Thread not owner,也就是wait、notify都只能在当前线程中访问,而其他线程不能访问
        try { //首先堵住左右两边的哲学家进行竞争
            //阻塞左边的哲学家
            if(this.getLeft().getPState() == 1) {
                this.getLeft().wait(); 
            }
            //阻塞右边的哲学家
            if(this.getRight().getPState() == 1) {
                this.getRight().wait();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } */

        this.setPState(2);  //将当前哲学家的状态置为2,从而阻塞左右两边的线程

        //阻塞左边的哲学家
        if(this.getLeft().getPState() == 1) {
            this.getLeft().PEat(); 
        }
        //阻塞右边的哲学家
        if(this.getRight().getPState() == 1) {
            this.getRight().PEat();
        } 

        System.out.println(this.name + " 正在吃饭....");
        try{
            Thread.sleep(300); // 吃饭时间0.3秒
        } catch(Exception e) {
            e.printStackTrace();
        }
        System.out.println(this.name + " 吃完了!");
        this.setPState(0); // 重新回到思考问题的状态
    }

    /**
     * 哲学家思考问题....
     */
    public void thinking() {
        System.out.println(this.name + "正在思考问题....");
    /*ERROR: 这里有问题,因为Thread.sleep不会让出对象锁,那么很可能会造成一个问题
     那就是当前对象释放了锁的同时,很快又得到了锁,从而锁住左右两边的对象,陷入死锁。 
        try{ 
            Thread.sleep(1000); //哲学家思考1秒
        } catch(Exception e) {
            e.printStackTrace();
        }  */
        //这里让哲学家干点事,不然执行太快

    /*#WARNING: 想通过写文件的方式,不行。因为CPU执行和文件读写是区分的。
        try {
            File file = new File("./question.txt");
            if(!file.exists()) {
                file.createNewFile();
            }
            FileWriter writer = new FileWriter(file);
            writer.append(this.name + " 正在思考 1 + 1 = ? \n");
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        } */

        double i = 0.0;
        while (i < 100000000) {
            i += 0.1;
        }

        this.setPState(1); //思考完了以后就饿了
    }

    /**
     * 给每一个哲学家一个信号量
     */
    public synchronized void PEat() {
        if(!this.canEat()) {
            try {
                this.wait();
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
    }

    public synchronized void VEat() {

    /*#ERROR: Exception Current Thread not owner,也就是wait、notify都只能在当前线程中访问,而其他线程不能访问    
        //唤醒左边的哲学家
        if(this.getLeft().getPState() == 1) {
            this.getLeft().notify(); 
        }
        //唤醒右边的哲学家
        if(this.getRight().getPState() == 1) {
            this.getRight().notify();
        } */
        this.notify();
    }

    public void run() {
        while(true) {
            thinking();
            this.PEat();
            this.eating();
            //唤醒左边的哲学家
            if(this.getLeft().getPState() == 1) {
                this.getLeft().VEat(); 
            }
            //唤醒右边的哲学家
            if(this.getRight().getPState() == 1) {
                this.getRight().VEat();
            }
        }
    }
}

测试类:

public class PhilosopherEatingQuestion {
    public static void main(String[] args) {
        /**
        * 任意时刻都只能有两个对象在吃饭。
        */
        Philosopher pher1 = new Philosopher("Lao Tzu");
        Philosopher pher2 = new Philosopher("Confucius");
        Philosopher pher3 = new Philosopher("Plato");
        Philosopher pher4 = new Philosopher("Aristotle");
        Philosopher pher5 = new Philosopher("Decare");

        //现在来排位置
        pher1.setLeft(pher5);
        pher1.setRight(pher1);

        pher2.setLeft(pher1);
        pher2.setRight(pher3);

        pher3.setLeft(pher2);
        pher3.setRight(pher4);

        pher4.setLeft(pher3);
        pher4.setRight(pher5);

        pher5.setLeft(pher4);
        pher5.setRight(pher1);

        // 启动
        pher1.start();
        pher2.start();
        pher3.start();
        pher4.start();
        pher5.start();
    }
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值