Dining Philosopher - 最后一个帖子没有正确终止

时间:2013-04-08 08:17:29

标签: java multithreading concurrency dining-philosopher

我写了这个Dining Philosopher代码,但最后一个帖子没有产生所需的“xxx已经完成他的晚餐”系列?我做错了什么?

似乎最后一个线程过早终止。

我将不胜感激。

import java.util.Random;



public class DiningPhilosophers {



    //An array holding all the chopsticks
    private final Chopstick[] chopsticks = new Chopstick[5];

    /*Constructor for the main class
    * Creates all the chopsticks 
    * Creates and starts all the threads*/
    public DiningPhilosophers(){
        putChopsticksOnTheTable();
        Thread t1 = new Thread(new Philosopher("First",this.chopsticks[4],this.chopsticks[0]));
        Thread t2 = new Thread(new Philosopher("Second",this.chopsticks[0],this.chopsticks[1]));
        Thread t3 = new Thread(new Philosopher("Third",this.chopsticks[1],this.chopsticks[2]));
        Thread t4 = new Thread(new Philosopher("Fourth",this.chopsticks[2],this.chopsticks[3]));
        Thread t5 = new Thread(new Philosopher("Fifth",this.chopsticks[3],this.chopsticks[4]));
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();


    }

    /*Initialise the chopsticks in the array*/
    private void putChopsticksOnTheTable(){
        for(int i = 0;i < chopsticks.length;i++)
        chopsticks[i]= new Chopstick(); 
    }

    public static void main(String[] args){
        new DiningPhilosophers();
    }
}


class Philosopher extends Thread{
private static final int EATING_TIME_LIMIT = 1000;
private static final int THINKING_TIME_LIMIT = 800;
private int EAT_TIMES = 5;
private final Random randomise = new Random();
private final Chopstick _leftChopstick;
private final Chopstick _rightChopstick;
private final String _name;
private State _state;

/* Enumeration class that holds 
* information about the possible 
* Philosopher's states 
*/
public enum State {
    EATING, THINKING
}

/*
* Main constructor for the Philosopher class
* @param name   the name of the Philosopher
* @param leftChopstick  the chopstick that is currently on the left of the Philosopher
* @param rightChopstick the chopstick currently on the right of the Philosopher
* 
*/
public Philosopher(String name, Chopstick leftChopstick, Chopstick rightChopstick) {

    this._leftChopstick = leftChopstick;
    this._rightChopstick = rightChopstick;
    this._name = name;

}

/*
* The method eat that uses two chopsticks. It blockes the two Chopstick
* objects so they could not be changed then it changes their state 
* as well as the state of the philosopher
* At the end of the method, the chopsticks' state is reverted and
* the Philosopher goes into the Thinking state 
*/
private void tryToEat() throws InterruptedException 
{       

     synchronized(_leftChopstick){
            while(_leftChopstick.inUse() || _rightChopstick.inUse())

                try{
                    //this.setPhilosopherState(Philosopher.State.WAITING);
                    _leftChopstick.wait();
                }catch (InterruptedException e){}
                    synchronized(_rightChopstick) {
                    try{
                        Thread.sleep(1);
                        _leftChopstick.pickUp();
                        System.out.println(_name + " picks up the left chopstick...");
                        _rightChopstick.pickUp();
                        System.out.println(_name + " picks up the right chopstick...");
                        eat();
                    }
                    finally {
                        _leftChopstick.putDown();
                        System.out.println(_name + " puts down the left chopstick...");
                        _rightChopstick.putDown(); 
                        System.out.println(_name + " puts down the right chopstick...");
                        //_leftChopstick.notify();
                        //_rightChopstick.notify();   
                    }
                    }
               }


    if(this.EAT_TIMES > 0)
            think();  

}

private void eat() throws InterruptedException
{
    setPhilosopherState(State.EATING);
    Thread.sleep(randomise.nextInt(EATING_TIME_LIMIT));        
    this.EAT_TIMES--;
    if(this.EAT_TIMES == 0)
        System.out.println("***************************" + _name + " has finished his dinner");
}

/*
* This method only changes the state 
* of the Philosopher to Thinking
*/
private void think() throws InterruptedException{
    setPhilosopherState(Philosopher.State.THINKING);
    Thread.sleep(randomise.nextInt(THINKING_TIME_LIMIT));
}

/*
* Set the current state of the Philosopher
*/
private void setPhilosopherState(State state){
    this._state = state;        

    if(_state == State.EATING)
        System.out.println ("*** " + _name + " is EATING for the " + (6 - EAT_TIMES) + " time!");
    else
        System.out.println( _name + " is THINKING...");
}

/*
* Get the current state of the Philosopher
*/
public State getPhilosopherState(){
    return _state;
}

/*
* The method is invoked with the start of the thread
* and runs the eat function for 10 times
*/
public void run(){
    while(this.EAT_TIMES > 0){
        try {
            tryToEat();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


}    

}


class Chopstick
{
   private boolean _inUse;

    /*
    * @return the current state of the chopstick
    */
    public boolean inUse(){
        return _inUse; 
    }

    /*
    * @param usedFlag the new state of the chopstick
    */
    public synchronized void pickUp()
    {           
        _inUse = true;
    }

    public void putDown()
    {
        _inUse = false;
        this.notify();
    }
}

3 个答案:

答案 0 :(得分:2)

这是一个很好的教训,说明为什么要同步对所有共享可变数据的访问。您的Chopstick有一个不易变的字段,但可以通过inUse由多个线程访问。

inUse

没有同步while (_leftChopstick.inUse() || _rightChopstick.inUse()) 可以返回通过数据竞争意外的值。结果,挂起的所有线程都停留在

rightChopstick.inUse()

如果您同步_leftChopstick.wait(); ,则应该正确完成。另外,我强烈建议同步inUse

中的所有方法

答案 1 :(得分:1)

在Chopstick类中,您还需要同步pickUp()方法,否则该值可能对其他线程不可见:

    public synchronized void pickUp() {
        _inUse = true;
    }

或者只是使用AtomicBoolean。

答案 2 :(得分:0)

没有必要致电

Thread t1 = new Thread(new Philosopher(...));

您可以改为呼叫

Thread t1 = new Philosopher(...);

要确保所有线程完成其工作,请调用其.join方法。这将在退出DiningPhilosophers构造函数之前等待线程死亡。

t1.start();
....
t5.start();
t1.join();
...
t5.join();