Семафорные проблемы в Java с философами столовой - PullRequest
5 голосов
/ 03 марта 2009

Я пытаюсь выучить основную часть семафора в задаче «Обедающий философ». Прямо сейчас у меня есть массив класса Chopstick, и у каждого Chopstick есть семафор с 1 доступным разрешением:

public class Chopstick
{
    Thread holder = null;
    private Semaphore lock = new Semaphore(1);

    public synchronized void take() throws InterruptedException
    {
        this.lock.acquire();
        holder = Thread.currentThread();

    }

    public synchronized void release()
    {   
        this.lock.release();
        holder = null;
    }
}

Переменная-держатель используется для функции, в которой я не уверен:

public synchronized void conditionalRelease()
{
    if (holder == Thread.currentThread())
    {
        holder = null;
        this.lock.release();
    }
}

Программа компилируется и запускается, но, похоже, возникают проблемы с выпуском палочек для еды. Иногда палочки для еды отпускаются, иногда нет. Когда они не выпускаются, программа в конце концов зависает, когда все палочки для еды взяты, и один философ голоден.

Вот код в классе Philosopher, чтобы выпустить палочку после случайного промежутка времени:

System.out.println(this.name + " is eating");
Thread.sleep(this.getRandTime());
System.out.println(this.name + " has finished eating");

rightChopstick.release();
System.out.println(this.name + " has released the right chopstick");
leftChopstick.release();
System.out.println(this.name + " has released the left chopstick");

Моя программа, например, выдает «Философ 0 закончил есть» и продолжает выполнение. Две другие строки никогда не выводятся, поэтому, очевидно, что-то не так с тем, как я выпускаю.

Любая помощь приветствуется.

Ответы [ 5 ]

8 голосов
/ 03 марта 2009

Я бы вынул ключевое слово «synchronized» из сигнатур вашего метода. Вы используете внешний механизм блокировки (семафор, в данном случае). Ключевое слово «synchronized» пытается получить блокировки, используя собственный мьютекс объекта. Теперь вы блокируете 2 ресурса, которые, как я подозреваю, могут привести к тупику.

1 голос
/ 26 ноября 2016

Убедитесь, что не используется какое-либо блокирующее или синхронизированное ключевое слово. Приведенный ниже код для Chop Stick отлично подходит для меня .. Не профессионал, но должен дать вам некоторое представление;

public class Chopstick {
private boolean inuse;
Semaphore sem;

public Chopstick(){

    inuse = false;
    sem = new Semaphore(1);
}
public void pickUp()
{
    try
    {
        while(inuse)
        {
            try
            {
                sem.acquire();

            }
            catch(InterruptedException e) {}
        }
        inuse = true;
    }catch(Exception e){}
}
public void putDown()
{
    try
    {
        inuse = false;
        sem.release();

    }
    catch (Exception e){}
}

}

1 голос
/ 03 марта 2009

Кажется немного странным, что вы оба блокируете палочку для еды и держите на ней семафор размера 1. Обычно семафор предоставляет билеты на ресурс, и если у вас только один билет, это фактически взаимное исключение, которое идентично блокировка (либо синхронизированный блок, либо объект блокировки). Вы можете подумать о том, чтобы сделать Chopstick самим объектом блокировки.

Я недавно написал пост в блоге о ресторанах на Java, если вам интересно, хотя на самом деле речь идет о том, как избежать тупика, используя другие стратегии.

1 голос
/ 03 марта 2009

Проблема в том, что когда в thread1 есть определенная палочка для еды, а другая пытается получить ту же самую, она будет ждать в take() -методе в строке this.lock.acquire();, но она NOT отпустит монитор на сам объект.

Если теперь thread1 пытается освободить палочку, он не может ввести release() -метод, поскольку он все еще заблокирован другим потоком, ожидающим в take(). Это тупик

0 голосов
/ 27 ноября 2013

Филосфер должен получить блокировку на обеих палочках перед началом еды и сначала подхватит влево, а затем подождет вправо, поэтому начните есть, чтобы метод запуска был синхронизирован. Следующие методы заставят его работать:

public synchronized void startEating() {
    leftChopstick.acquire();
    rightChopstick.acquire();
}

public void finishEating(int id) {
    leftChopstick.release();
    rightChopstick.release();
}
...