У меня есть этот код, который представляет собой симуляцию проблемы производителя-потребителя с использованием одного производителя и нескольких потребителей в общем объекте класса 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 ().




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