Java путаница BlockingQueue - PullRequest
       79

Java путаница BlockingQueue

1 голос
/ 07 августа 2020

Сейчас я читаю о Java BlockingQueue, и этот пример приводится на многих веб-сайтах для простой реализации BlockingQueue. Код простой, но я немного запутался. Например, допустим, мы заполняем очередь, а затем пытаемся поставить в нее еще 3 раза. Это заставит 3 потока ждать. Затем, когда мы вызываем dequeue, код в методе dequeue вводит второй оператор if и уведомляет все потоки. Не будет ли это означать, что все 3 ожидающих потока добавят узлы в очередь? Значит, у нас будет на 2 узла больше лимита? Я почти уверен, что что-то здесь неправильно понял, поэтому могу использовать небольшое объяснение.

public class BlockingQueue {

  private List queue = new LinkedList();
  private int  limit = 10;

  public BlockingQueue(int limit){
    this.limit = limit;
  }


  public synchronized void enqueue(Object item)
  throws InterruptedException  {
    while(this.queue.size() == this.limit) {
      wait();
    }
    this.queue.add(item);
    if(this.queue.size() == 1) {
      notifyAll();
    }
  }


  public synchronized Object dequeue()
  throws InterruptedException{
    while(this.queue.size() == 0){
      wait();
    }
    if(this.queue.size() == this.limit){
      notifyAll();
    }

    return this.queue.remove(0);
  }

}

Ответы [ 2 ]

1 голос
/ 07 августа 2020

Обратите внимание, что wait() из .enqueue() находится внутри l oop. Любой пробужденный поток будет повторно проверять разрешение на добавление элемента, и поскольку только один поток может выполнять синхронизированный метод за раз, проблем не возникнет - одному потоку повезет вставить элемент, другие продолжают ждать после неудачной повторной проверки.

1 голос
/ 07 августа 2020

Нет, только один добавит узел. Обратите внимание, что ваш wait -вызов в enqueue находится внутри al oop:

    while(this.queue.size() == this.limit) {
      wait();
    }

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

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

...