Обедающих Философов


Я только что закончил мое решение на обеденный философский проблема, но я не уверена, с моим кодом, потому что я еще новичок в параллелизм мира. Я был бы признателен, если бы вы могли оставить мне некоторую обратную связь.

Вот мой основной класс:

public class DiningPhilosophersTable {

    //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 DiningPhilosophersTable(){
        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 DiningPhilosophersTable();
    }
}

Вот философ класса:

public class Philosopher extends Thread{

    private static final int MAX_EATING_TIME = 1000;
    private static final int MAX_THINKING_TIME = 800;
    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, WAITING
    }

    /*
    * 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) {
        System.out.println(name +"Started");
        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 eat() throws InterruptedException {

        synchronized(_leftChopstick){
        while(_leftChopstick.isUsed() || _rightChopstick.isUsed())      
            try{
                this.setPhilosopherState(Philosopher.State.WAITING);
                _leftChopstick.wait();
            }catch (InterruptedException e){}
                synchronized(_rightChopstick) {
                try{
                    Thread.sleep(1);
                    _leftChopstick.setUsed(true);
                    _rightChopstick.setUsed(true);
                    this.setPhilosopherState(Philosopher.State.EATING);
                    Thread.sleep(randomise.nextInt(MAX_EATING_TIME));
                }
                finally {
                    _leftChopstick.setUsed(false);
                    _rightChopstick.setUsed(false); 
                    _leftChopstick.notify();
                    _rightChopstick.notify();   
                }
                }
            }

        think();
    }

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

    /*
    * Set the current state of the Philosopher
    */
    private void setPhilosopherState(State state){
        this._state = state;
        System.out.println(System.currentTimeMillis() +":"+ _state +", "+ _name+";");
    }

    /*
    * 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(){
        for(int i =0; i< 10;i++){
            try {
                eat();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("Succesfully finished: " +_name);
    }
}

И последний класс:

public class Chopstick {

    private boolean _isUsed;

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

    /*
    * @param usedFlag the new state of the chopstick
    */
    public void setUsed(boolean usedFlag){
        _isUsed = usedFlag;
    }
}


15672
10
задан 28 ноября 2011 в 06:11 Источник Поделиться
Комментарии
3 ответа

Только быстрое Примечание:

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

Здесь вы должны синхронизировать на _rightChopstick слишком, поскольку используется может вызываться из других потоков одновременно.


[...] синхронизацию не влияет, если обе операции чтения и записи будут синхронизированы.

От эффективная Java 2-е издание, пункт 66: синхронизировать доступ к разделяемым изменяемым данным.


Замок-это не только взаимоисключение, но и видимость памяти.
Чтобы убедиться, что все темы просмотреть наиболее актуальную значений общей Мутабельный
переменные, читать и писать потоки должны синхронизировать на общий замок.

Из на Java параллелизм на практике, 3.1.3. Замок и видимость.

Другой (и лучше) всего будет использовать AtomicBooleanС.

5
ответ дан 28 ноября 2011 в 01:11 Источник Поделиться

Вместо класса палочки вы могли бы использовать Java.утиль.одновременно.замки.Замок напрямую.
Возможной реализации (не голодание бесплатно)

private void eat() {
if (_leftChopstick.tryLock()) {
try {
if (_rightChopStick.tryLock()) {
try {
Thread.sleep(randomise.nextInt(MAX_EATING_TIME));
}
finally {
_rightChopStick.unlock();
}
}
}
finally {
_leftChopstick.unlock();
}
}
think();
}

2
ответ дан 8 декабря 2011 в 08:12 Источник Поделиться

Теоретически может произойти взаимоблокировка в коде(есть небольшая вероятность, особенно эта вероятность возрастает, когда вы запустите ваш код на одном ядре системы).

Предположим такую ситуацию, в которой философы нумеруются по часовой стрелке.
1-й философами получить блокировку на левую палочку, а затем проверить, если левый и правый chopstics бесплатно(при запуске они могут быть свободны) и сразу после этого 2-го философа(который сидит справа первый) и получить блокировку на левую палочку. Итак, 1-ый философ будет заблокирован на мониторе его правая палочка по.

3-й и 4-й философы повторить описанный процесс после 1-го и 2-го философа.

И ЛСТ 5-й философов будет пытаться получить блокировку на левую палочку монитора и будут блокированы на правой палочками монитор при проверке его наличия.

Эта вероятность достаточно мала, но она может произойти. И если это может произойти тогда, я думаю, это не действительный код. Propability тупика должен быть равен 0, тогда это будет корректный код.

0
ответ дан 16 декабря 2012 в 12:12 Источник Поделиться