【问题标题】:Dining Philosopher - Last thread not terminating properlyDining Philosopher - 最后一个线程没有正确终止
【发布时间】:2013-04-08 08:17:29
【问题描述】:

我写了这个餐饮哲学家的代码,但最后一个线程没有产生所需的“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();
    }
}

【问题讨论】:

  • 我建议只发布相关部分,而不是转储所有代码。
  • 或者,更好的是,将代码提炼到重现问题的绝对最低限度,并将其完整发布。
  • 感谢您的建设性但无关紧要的批评。

标签: java multithreading concurrency dining-philosopher


【解决方案1】:

这是一个很好的课程,告诉您为什么应该同步对所有共享可变数据的访问。您的Chopstick 有一个非易失性字段,但可由多个线程通过inUse 访问。 inUse 在没有同步的情况下被引用的唯一位置是

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

没有同步rightChopstick.inUse() 可能会通过数据竞争返回意外的值。结果,所有挂起的线程都卡在了

_leftChopstick.wait();

如果您同步inUse,您应该可以正确完成它。另外我强烈建议同步Chopstick中的所有方法

【讨论】:

    【解决方案2】:

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

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

    或者简单地使用 AtomicBoolean。

    【讨论】:

    • @John Vint 也有同样的建议,而且很有效。很抱歉,我不得不为他打分,因为他是第一位的。不过谢谢。
    • 我提前 7 小时来了 :)
    【解决方案3】:

    无需调用

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

    您可以改为调用

    Thread t1 = new Philosopher(...);
    

    为确保所有线程完成其工作,请调用它们的 .join 方法。这将在退出 DiningPhilosophers 构造函数之前等待线程终止。

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

    【讨论】:

    • 好点。但这并不能解决问题。但感谢您指出这一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多