Ожидание / Уведомление о блокировке - PullRequest
1 голос
/ 15 декабря 2011

У меня есть очередь с некоторым механизмом блокировки в методах «Добавить» и «Получить», где первый поток добавляет данные, а второй поток получает данные.

public synchronized MyObj getData() {               
    synchronized (myLock) {
        synchronized (this) {
            if (isEmpty()) {                
                wait(0);                    
            }
        }       


        return getData();           
    }
}

public synchronized void addData(MyObj data) {
    if (!isFull()) {
        putData(data);
        synchronized (this) {
            notify();
        }
    }
}

В приведенном выше коде, если первый поток пытается получить данные, а очередь пуста, я помещаю в ожидание через wait (0), пока другой поток не добавит данные в очередь для освобождения от ожидания через notify ().

Теперь я хочу добавить еще одну «блокировку», когда очередь заполнена, и кто-то пытается добавить к ней больше данных:

public synchronized MyObj getData() {               
    synchronized (myLock) {
        synchronized (this) {
            if (isEmpty()) {                
                wait(0);                    
            }
        }       

        synchronized (this) {
            notify();
        }
        return getData();           
    }
}

public synchronized void addData(MyObj data) {
    synchronized (myLock) {
        synchronized (this) {
            if (isFull()) {
                wait(0);
            }
        }
    }

    synchronized (this) {
        notify();
        }
        PutData(data);
}

Результат не тот, который я ожидаю, я думаю, что я получаю тупиковую блокировку, потому что процесс застрял.

ОБНОВЛЕНИЕ

Вот как я получаю данные:

queueSize--;
startPointer = (startPointer + 1) % mqueueSize;
data = (String) queue[startPointer];

вот как я добавляю данные

  queueSize++;
  endPointer = (endPointer + 1) % mqueueSize;
  queue[endPointer] = data;

public synchronized boolean isEmpty() {
        return queueSize== 0;
    }

    public synchronized boolean isFull() {
        return queueSize== mqueueSize;
    }

Ответы [ 5 ]

3 голосов
/ 15 декабря 2011

Почему у вас есть три synchronized заявления?wait(0) снимает блокировку только с this, поэтому оставьте ее и сбросьте synchronized из метода и synchronized(myLock).

Каждый раз, когда вы вызываете wait для какого-либо объекта (в данном случаеВы вызываете this), блокировка этого объекта автоматически снимается, чтобы позволить другому потоку продолжить.Но вы никогда не звоните и ждите на myLock (и не должны, потому что вы уже звоните на this).Эта часть является избыточной и вызывает взаимоблокировку.

Рассмотрим этот сценарий: поток, который предполагается добавить, берет блокировку на myLock, но находит очередь заполненной, поэтому он ждет.Это ожидание не снимает блокировку на myLock.Другой поток хочет получить данные, но не может войти в блок synchronized, поскольку первый поток не снял блокировку на myLock.

Вывод: удалите блоки synchronized(myLock).

1 голос
/ 15 декабря 2011

Удалите ключевое слово synchronized из своих сигнатур методов, поскольку это означает, что вы держите монитор this для всего вызова метода - блоки synchronized(this) просто избыточны.

РЕДАКТИРОВАТЬ:

... Затем позвоните и подождите myLock, а не this.И полностью забудьте о синхронизации на this.Это происходит потому, что во время ожидания (на this в вашем текущем коде) вы не снимаете блокировку myLock, поэтому другой поток не может получить notify().

1 голос
/ 15 декабря 2011

Почему вы не заглядываете в java.util.BlockingQueue .Возможно, это будет полезно в вашей ситуации.

В частности, обратите внимание на java.util.LinkedBlockingQueue , где, если вы укажете емкость очереди в конструкторе, то очередь заблокируется.

0 голосов
/ 16 декабря 2011

Как уже упоминалось, в вашем коде слишком много аннотаций synchronized.Кроме того, условие wait включено проверяется в условном выражении if, но в идеале его следует проверять в цикле while, чтобы избежать ложных пробуждений .Вот схема кода, который их исправляет.

// _isEmpty and _getData are private unsynchronized methods
public MyData get() throws InterruptedException {
  // wait and notify should be called from a block
  // synchronized on the same lock object (here myLock)       
  synchronized (myLock) {
    // the condition should be tested in a while loop
    // to avoid issues with spurious wakeups
    while (_isEmpty()) {
      // releases the lock and wait for a notify to be called
      myLock.wait();
    }
    // when control reaches here, we know for sure that
    // the queue is not empty
    MyData data = _getData();
    // try to wake up all waiting threads - maybe some thread
    // is waiting for the queue not to be full
    myLock.notifyAll();
  }
}

// _isFull and _putData are private unsynchronized methods
public void put(MyData obj) throws InterruptedException {
  synchronized (myLock) {
    while (_isFull()) {
      myLock.wait();
    }
    _putData(obj);
    myLock.notifyAll();
  }
}
0 голосов
/ 15 декабря 2011

Заменить if на while. Не повредит дважды проверить, действительно ли коллекция стала не пустой / не полной.

Тебе не нужны два замка. Одиночная блокировка будет работать почти так же хорошо и должна быть намного проще.

public synchronized T get()
{
    while(isEmpty())
        wait(0);

    notifyAll();

    return super.get();

}

public synchronized put(T t)
{

    while(isFull())
        wait(0);

    super.put(t);

    notifyAll();

}

Все потоки проснутся, когда что-то изменится. Но если они не могут выполнять свою работу, они будут wait на следующие notify.

...