Многократный потребительский производитель в Java
Я пытался создать 2 темы для потребителя и 2 темы для производителя. Все 4 темы связаны с одним ресурсом. Двое из них пытаются потреблять ресурсы, а двое пытаются добывать.
Ниже приведен код
package com.threading;
import java.util.ArrayList;
import java.util.List;
public class TestConsumerProducer2 {
protected static int maxSize = 2;
static class Consumer implements Runnable {
List<Integer> goods;
public Consumer(List<Integer> goods) {
this.goods = goods;
}
public void consume() {
synchronized (goods) {
if (goods.size() <= 0) {
try {
goods.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " >>>> consuming >>>" + goods.remove(0));
goods.notifyAll();
}
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
consume();
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
static class Producer implements Runnable {
List<Integer> goods;
public Producer(List<Integer> goods) {
this.goods = goods;
}
public void produce(int i) {
synchronized (goods) {
if (goods.size() >= maxSize) {
try {
goods.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ">>> producing >> " + i);
goods.add(i);
goods.notifyAll();
}
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
produce(i);
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
List<Integer> goods = new ArrayList<>();
Consumer consumer = new Consumer(goods);
Producer producer = new Producer(goods);
Thread consumerWorker1 = new Thread(consumer);
Thread consumerWorker2 = new Thread(consumer);
Thread prroducerWorker1 = new Thread(producer);
Thread prroducerWorker2 = new Thread(producer);
consumerWorker1.start();
consumerWorker2.start();
prroducerWorker1.start();
prroducerWorker2.start();
try {
consumerWorker1.join();
consumerWorker2.join();
prroducerWorker1.join();
prroducerWorker2.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Job completed >>>>");
}
}
Выход программы
Нить-2 >>> производит >> 0
Тема-1 >>>> Потребляющая >>>0
Нить-3>>> производит >> 0
Исключение в потоке "Thread-0" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 at java.util.ArrayList.rangeCheck(ArrayList.java:657) в java.util.ArrayList.remove(ArrayList.java:496) в com.threading.TestConsumerProducer2$Consumer.consume(TestConsumerProducer2.java:27) в com.threading.TestConsumerProducer2$Consumer.run(TestConsumerProducer2.java:35) в java.lang.Thread.run(Thread.java:7) Нить-2 >>> производит >> 1
Тема-1 >>>> Потребляющая >>>0
Нить-3>>> производит >> 1
Thread-1 >>>> потребляющий >>> 1
Нить-2 >>> производит >> 2
Thread-1 >>>> потребляющий >>> 1
Нить-3>>> производит >> 2
Thread-1 >>>> потребляющий >>> 2
Нить-2 >>> производит >> 3
Thread-1 >>>> потребляющий >>> 2
Нить-2 >>> производит >> 4
Нить-3>>> производит >> 3
Thread-1 >>>> потребляющий >>>3
Нить-2 >>> производит >> 5
Тема-1 >>>> Потребляющая >>>4
Нить-3>>> производит >> 4
Thread-1 >>>> потребляющий >>>3
Нить-2 >>> производит >> 6 Нить-1 >>>> потребляет >>>5
Нить-2 >>> производит >> 7
Нить-3>>> производит >> 5
Постановка проблемы: Почему ни один из потоков не исполняется 10 раз? Где в коде тупиковая ситуация? Почему существует исключение IndexOutOfBoundsException, когда объект Goods заблокирован потребительским потоком и если размер <=0, он должен перейти в состояние ожидания?
1 ответ
Изменение вхождения if
в while
в вашем коде исправляет проблему.
В руководстве по охраняемым блокам на сайте Oracle есть соответствующий совет:
Примечание. Всегда вызывайте wait внутри цикла, который проверяет ожидаемое условие. Не думайте, что прерывание было для конкретного условия, которого вы ждали, или что условие все еще выполняется.
(Под прерыванием они подразумевают возврат из ожидания, не обязательно фактическое прерывание, исходящее от того, кто вызывает Thread.interrupt.)
Ключевые моменты:
Поток знает содержимое списка товаров, только если он удерживает блокировку во время проверки.
Вызов ожидания освобождает блокировку, позволяя другим потокам прогрессировать, пока этот не работает.
Как только поток снимает блокировку, любые ранее сделанные им проверки состояния списка товаров становятся недействительными.
Как только поток возвращается из ожидания, он снова получает блокировку, но поток должен повторно оценить проверку состояния, в противном случае он воздействует на устаревшую информацию. Многое могло случиться между временем, когда поток последний раз проверял условие, и текущим временем. Вы получаете IllegalArgumentException, потому что текущий поток предполагает, что есть что-то, что другой поток удалил, в то время как текущий поток ожидал.
package com.threading;
import java.util.ArrayList;
import java.util.List;
public class TestConsumerProducer2 {
protected static int maxSize = 2;
static class Consumer implements Runnable {
List<Integer> goods;
public Consumer(List<Integer> goods) {
this.goods = goods;
}
public void consume() {
synchronized (goods) {
while (goods.size() <= 0) {
try {
goods.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " >>>> consuming >>>" + goods.remove(0));
goods.notifyAll();
}
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
consume();
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
static class Producer implements Runnable {
List<Integer> goods;
public Producer(List<Integer> goods) {
this.goods = goods;
}
public void produce(int i) {
synchronized (goods) {
while (goods.size() >= maxSize) {
try {
goods.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ">>> producing >> " + i);
goods.add(i);
goods.notifyAll();
}
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
produce(i);
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
List<Integer> goods = new ArrayList<>();
Consumer consumer = new Consumer(goods);
Producer producer = new Producer(goods);
Thread consumerWorker1 = new Thread(consumer);
Thread consumerWorker2 = new Thread(consumer);
Thread prroducerWorker1 = new Thread(producer);
Thread prroducerWorker2 = new Thread(producer);
consumerWorker1.start();
consumerWorker2.start();
prroducerWorker1.start();
prroducerWorker2.start();
try {
consumerWorker1.join();
consumerWorker2.join();
prroducerWorker1.join();
prroducerWorker2.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Job completed >>>>");
}
}
Код успешно завершен, вывод следующий:
C:\>java com.threading.TestConsumerProducer2
Thread-2>>> producing >> 0
Thread-1 >>>> consuming >>>0
Thread-3>>> producing >> 0
Thread-0 >>>> consuming >>>0
Thread-2>>> producing >> 1
Thread-3>>> producing >> 1
Thread-0 >>>> consuming >>>1
Thread-1 >>>> consuming >>>1
Thread-2>>> producing >> 2
Thread-3>>> producing >> 2
Thread-0 >>>> consuming >>>2
Thread-1 >>>> consuming >>>2
Thread-2>>> producing >> 3
Thread-0 >>>> consuming >>>3
Thread-3>>> producing >> 3
Thread-1 >>>> consuming >>>3
Thread-2>>> producing >> 4
Thread-0 >>>> consuming >>>4
Thread-3>>> producing >> 4
Thread-1 >>>> consuming >>>4
Thread-2>>> producing >> 5
Thread-0 >>>> consuming >>>5
Thread-3>>> producing >> 5
Thread-1 >>>> consuming >>>5
Thread-2>>> producing >> 6
Thread-0 >>>> consuming >>>6
Thread-3>>> producing >> 6
Thread-1 >>>> consuming >>>6
Thread-2>>> producing >> 7
Thread-0 >>>> consuming >>>7
Thread-3>>> producing >> 7
Thread-1 >>>> consuming >>>7
Thread-2>>> producing >> 8
Thread-0 >>>> consuming >>>8
Thread-3>>> producing >> 8
Thread-1 >>>> consuming >>>8
Thread-2>>> producing >> 9
Thread-0 >>>> consuming >>>9
Thread-3>>> producing >> 9
Thread-1 >>>> consuming >>>9
Job completed >>>>