многопоточная задача про философов

 
0
 
Java
ava
Pawl | 27.03.2013, 13:45
Доброго времени суток, возможно, все слышали про сабж - это там, где философы сидят в кружок за столом, покушают спагетти, потом подумают, потом опять покушают и т. д., пока спагетти не кончатся. Проблема состоит в том, что спагетти надо есть 2-мя вилками, а количество вилок на столе совпадает с количеством философов. Поэтому некоторые философы, для того чтобы поесть, должны ждать освобождения вилок. Обычно эту задачу приводят, как демонстрацию синхронизации и дедлоков. Я сперва ее реализовал с использованием synchronized и volatile там, где посчитал нужным, а потом для интереса их убрал. Так вот, это нисколько не повлияло на работу программы! Т. е. никакой одновременности в поедании спагетти сразу всеми философами не случилось, хотя, на сколько я понимаю, она должна быть. Вот исходный код классов программы:
Вилка - 

public class Fork {
    private boolean free = true;

    public boolean isFree() {
        return free;
    }

    public void setFree(boolean free) {
        this.free = free;
    }
}

тарелка - 

public class Plate {
    private int spaghetti;

    public Plate(int spaghetti) {
        this.spaghetti = spaghetti;
    }

    public void getSpaghetti() {
        spaghetti--;
    }

    public boolean isEmpty() {
        return spaghetti == 0;
    }
}

философ - 

public class Philosopher implements Runnable {
    // левая и правая вилки
    private Fork f1, f2;
    // тарелка со спагетти
    private Plate plate;
    private String name;

    public Philosopher(String name, Plate plate, Fork f1, Fork f2) {
        this.f1 = f1;
        this.f2 = f2;
        this.plate = plate;
        this.name = name;
    }
    // философ ест
    private void eat() {
        // берет вилки
        f1.setFree(false);
        f2.setFree(false);
        // ест спагетти
        for (int i = 0; i < 5; i++) {
            if (plate.isEmpty()) {
                break;
            }
            System.out.printf("%-10s %s %n", name, " eat");
            plate.getSpaghetti();
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
            }
        }
        // кладет вилки на стол
        f1.setFree(true);
        f2.setFree(true);
    }

    // процесс питания
    @Override
    public void run() {
        // пока тарелка не пуста
        while (!plate.isEmpty()) {
            // если вилки свободны, съесть немного спагетти
            if (f1.isFree() && f2.isFree()) {
                eat();
            }
            // подумать
            think();
        }
        // встать из-за стола
        System.out.printf("%-10s %s %n", name, " DONE ");
    }

    // философ думает над непустой тарелкой
    public void think() {
        if (plate.isEmpty()) {
            return;
        }
        System.out.printf("%-10s %s %n", name, " think");
        try {
            Thread.sleep(2);
        } catch (InterruptedException e) {
        }
    }

    // запуск процесса
    public void start() {
        new Thread(this).start();
    }
}

стол - 

public class Table {
    // накрываем стол, рассаживаем по кругу философов и вперед
    public static void main(String[] args) {
        int spaghetti = 50;
        Fork f1 = new Fork();
        Fork f2 = new Fork();
        Fork f3 = new Fork();
        Fork f4 = new Fork();
        Plate p1 = new Plate(spaghetti);
        Plate p2 = new Plate(spaghetti);
        Plate p3 = new Plate(spaghetti);
        Plate p4 = new Plate(spaghetti);
        new Philosopher("Socrat", p1, f1, f2).start();
        new Philosopher("Platon", p2, f2, f3).start();
        new Philosopher("Camu",   p3, f3, f4).start();
        new Philosopher("Kant",   p4, f4, f1).start();
    }
}

Я считаю, что в методе eat класса Philosopher два философа, впринципе, могут одновременно захватить одну и ту же вилку, т. к. доступ к вилкам не синхронизирован. Кроме того, значение переменной free у вилки, по идее, кэшируется потоками, т. е. другой поток может не сразу узнать ее текущее значение. Изначально я объявил метод eat как synchronized, а переменную free - как volatile. Потом убрал эти объявления, результат работы остался тем же. Буду благодарен, если объясните почему. Спасибо! 
Ответы (2)
ava
Pawl | 27.03.2013, 14:31 #
На 'дцатом запуске мои подозрения подтвердились! smile Тема закрыта.
ava
batigoal | 27.03.2013, 15:46 #
Pawl, если бы она проявлялась каждый раз, то проблемы многопоточности не были бы такими геморройными =)

Я не смотрел текст, но обычно можно повысить вероятность до почти 100% за счет sleep-ов в выполняемых потоках и паузах между их запуском (например, секунда задержки в цикле-worker'е и полсекунды между их стартом).
Зарегистрируйтесь или войдите, чтобы написать.
Фирма дня
Вы также можете добавить свою фирму в каталог IT-фирм, и публиковать статьи, новости, вакансии и другую информацию от имени фирмы.
Подробнее
Участники
  batigoal ava  Pawl
advanced
Отправить