Производитель с несколькими потребителями не работает с notify ()

У меня есть этот код, который представляет собой симуляцию проблемы производителя-потребителя с использованием одного производителя и нескольких потребителей в общем объекте класса Q. Я использую notify () здесь, а не notifyAll (), поскольку мне нужно понять, почему этот код переходит в состояние тупика или бесконечного ожидания.

Моя точка зрения такова: если есть один производитель и несколько потребителей, тогда notify () вызовет только один поток в состоянии ожидания, а rest останется в состоянии wait (). Затем производитель снова возобновит работу, и, следовательно, код продолжит выполнение.

Наблюдение: здесь все производители и потребители потоков переходят в состояние бесконечного ожидания. Код показан ниже:

public class ProdConsProb {
    public static void main(String[] args) {
        Q q = new Q();
        Thread producerThread = new Thread(new Producer(q), "producerThread");
        Thread consumerThread = new Thread(new Consumer(q), "Consumer1");
        Thread consumerAnotherThread = new Thread(new Consumer(q), "Consumer2");
        Thread consumerYetAnotherThread = new Thread(new Consumer(q), "Consumer3");
        producerThread.start();
        consumerThread.start();
        consumerAnotherThread.start();
        consumerYetAnotherThread.start();
    }
}

class Producer implements Runnable {
    Q q;

    public Producer(Q q) {
        this.q = q;
    }

    @Override
    public void run() {
        int i = 0;
        while (true)
            try {
                q.setN(i++);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

    }

}

class Consumer implements Runnable {
    Q q;

    public Consumer(Q q) {
        this.q = q;
    }

    @Override
    public void run() {
        while (true)
            try {
                q.getN();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    }

}

class Q {
    private int n = 0;
    boolean valueSet = false;

    public synchronized int getN() throws InterruptedException {
        while (!valueSet) 
        {
            wait();
        }
        valueSet = false;
        notify();
        return n;
    }

    public synchronized void setN(int n) throws InterruptedException {
        while (valueSet == true)
        {
            wait();
        }
        this.n = n;
        valueSet = true;
        notify();

    }
}

Я добавил несколько систем. Сгенерированные журналы показаны ниже:

producerThread :: SetN : Valueset is false
producerThread :: Producer inserted 0
producerThread :: SetN : Valueset after is true
producerThread :: SetN : Valueset is true
producerThread wait() ------------ Active 
producerThread :: SetN :Wait() Valueset is true
Consumer3  Start :: GetN : Valueset is true
Consumer3 :: Consumer read 0
Consumer3  End :: GetN : Valueset after is false
Consumer3  Start :: GetN : Valueset is false
Consumer3 wait() ------------ Active 
Consumer3 :: GetN :Wait() Valueset is false
Consumer2  Start :: GetN : Valueset is false
Consumer2 wait() ------------ Active 
Consumer2 :: GetN :Wait() Valueset is false
Consumer1  Start :: GetN : Valueset is false
Consumer1 wait() ------------ Active 
Consumer1 :: GetN :Wait() Valueset is false
producerThread wait()   ------------- left 
producerThread :: Producer inserted 1
producerThread :: SetN : Valueset after is true
producerThread :: SetN : Valueset is true
producerThread wait() ------------ Active 
 -->>   producerThread :: SetN :Wait() Valueset is true
        Consumer3 wait() left 
        Consumer3 :: Consumer read 1
        Consumer3  End :: GetN : Valueset after is false
        Consumer3  Start :: GetN : Valueset is false
        Consumer3 wait() ------------ Active 
        Consumer3 :: GetN :Wait() Valueset is false
 ????   Consumer2 wait() left 
        Consumer2 wait() ------------ Active 
        Consumer2 :: GetN :Wait() Valueset is false

Странная вещь здесь заключалась в том, что когда производитель уведомляет после вставки 1, потребитель 3 считывает данные и уведомляет производителя. Теперь производитель 3 должен выполнить запуск из своего wait (), но поток customer2 покидает свою wait () и возвращается к wait ().

Примечание. Этот код работает с notifyAll (), но я ищу причину, по которой он не работает с notify ().

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
74
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это не удается, потому что производители и потребители ожидают одного и того же монитора, а внутренние блокировки не поддерживают отдельные условия. Если уведомление происходит, его может получить либо производитель, либо потребитель. Но данное уведомление будет применимо только к одному или другому. Когда один получает уведомление, что только другой может действовать, это уведомление не приносит никакой пользы: уведомленный поток просыпается, обнаруживает, что условие, которое он ожидает, все еще ложно, и возвращается в режим ожидания.

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

В этом есть смысл. Я читал о ReentrantLock, и их реализация предполагает то же самое. Спасибо. Я попытаюсь реализовать описанное выше, используя ReentrantLock, а затем предложу решение.

Nishant_Singh 04.05.2018 21:17

Другие вопросы по теме