2013-04-08 36 views
0

我寫了這個餐飲哲學家代碼,但最後一個線程不會產生所需的「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(); 
    } 
} 
+0

而不是傾銷所有的代碼,我建議只發布相關部分。 – Zyerah 2013-04-08 08:20:29

+0

或者,甚至更好的是,將代碼提取到絕對最小值以重現問題,並將其完整發布。 – NPE 2013-04-08 08:22:01

+0

感謝您的建設性但無關緊要的批評。 – user1872371 2013-04-09 04:42:40

回答

2

這是一個很好的教訓,你爲什麼應該同步訪問所有共享的可變數據。您的Chopstick有一個不易變的字段,但可通過inUse由多個線程訪問。其中inUse正在不同步引用的唯一地方是在

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

如果沒有同步rightChopstick.inUse()可以返回通過數據意外種族值。其結果是被掛的所有線程都在

_leftChopstick.wait(); 

卡住如果同步inUse,你應該有它正確地整理。我也強烈建議同步所有的方法Chopstick

+0

謝謝。我在Chopstick中同步所有內容,並且它可以工作! – user1872371 2013-04-09 04:44:54

1

在筷子類,你也需要同步pickUp()方法,否則該值可能是不可見的其他線程:

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

或者乾脆使用的AtomicBoolean。

+0

@John Vint有相同的建議,它的工作原理。我很抱歉,我必須先標記他是因爲他先來的信用卡。但是,謝謝。 – user1872371 2013-04-09 04:44:13

+0

我約7小時前來:) – 2013-04-09 07:02:15

0

無需調用

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

您可以改爲調用

Thread t1 = new Philosopher(...); 

爲了確保所有線程完成自己的工作,叫他們。加入方法。這將等待線程在退出DiningPhilosophers構造函數之前死亡。

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

好點。但這並不能解決問題。但謝謝指出。 – user1872371 2013-04-09 04:41:36