我写了这个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();
}
}
答案 0 :(得分:2)
这是一个很好的教训,说明为什么要同步对所有共享可变数据的访问。您的Chopstick
有一个不易变的字段,但可以通过inUse
由多个线程访问。 1}}在没有同步的情况下被引用的唯一位置是
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();