BlockingQueue отлично подходит, когда вы хотите избежать сложностей, связанных с операторами wait–notify. Этот BlockingQueue можно использовать для решения проблемы производителя-потребителя, а также для данного примера blow. Поскольку эта проблема хорошо известна каждому программисту, я не буду вдаваться в подробности ее описания.
Как BlockingQueue вписывается в решение
Любое эффективное решение проблемы производителя-потребителя должно контролировать вызов метода put() производителя, который генерирует ресурс, и метода take() потребителя, который потребляет ресурс. Как только вы достигнете этого контроля блокировки методов, вы решите проблему.
Java предоставляет встроенную поддержку для управления такими вызовами методов, когда один поток создает ресурсы, а другой их потребляет — через BlockingQueue. Интерфейс Java BlockingQueue в пакете java.util.concurrent представляет собой очередь, в которую можно безопасно помещать и из которой можно извлекать экземпляры.
BlockingQueue — это конструкция, в которую один поток помещает ресурсы, а другой поток извлекает из нее ресурсы.
Это именно то, что нужно для решения проблемы производителя-потребителя. Давайте решим проблему сейчас!!
Использование BlockingQueue для решения проблемы «Производитель-потребитель»
Продюсер
Ниже представлен код для потока производителя.
class Producer implements Runnable{protected BlockingQueue<Object> queue;Producer(BlockingQueue<Object> theQueue) {this.queue = theQueue;}public void run(){try{while(true){Object justProduced = getResource();queue.put(justProduced);System.out.println("Produced resource - Queue size now = " + queue.size());}}catch(InterruptedException ex){System.out.println("Producer INTERRUPTED");}}Object getResource(){try{Thread.sleep(100); // simulate time passing during read}catch(InterruptedException ex){System.out.println("Producer Read INTERRUPTED");}return new Object();}}
Здесь поток-производитель создает ресурс(т. е. объект) и помещает его в очередь. Если очередь уже заполнена(максимальный размер — 20); то он будет ждать — пока поток-потребитель не вытащит из нее ресурс. Таким образом, размер очереди никогда не превысит максимум, т. е. 20.
Потребитель
Ниже представлен код для потребительского потока.
class Consumer implements Runnable{protected BlockingQueue<Object> queue;Consumer(BlockingQueue<Object> theQueue) {this.queue = theQueue;}public void run() {try{while(true){Object obj = queue.take();System.out.println("Consumed resource - Queue size now = " + queue.size());take(obj);}}catch(InterruptedException ex){System.out.println("CONSUMER INTERRUPTED");}}void take(Object obj){try{Thread.sleep(100); // simulate time passing}catch(InterruptedException ex){System.out.println("Consumer Read INTERRUPTED");}System.out.println("Consuming object " + obj);}}
Поток-потребитель извлекает ресурс из очереди, если он там есть, в противном случае он будет ждать, а затем снова проверит, поместил ли в него что-нибудь производитель.
Тестирование Производитель Потребитель Решение
Теперь давайте протестируем компоненты производителя и потребителя, написанные выше.
public class ProducerConsumerExample{public static void main(String[] args) throws InterruptedException{int numProducers = 4;int numConsumers = 3;BlockingQueue<Object> myQueue = new LinkedBlockingQueue<>(20);for(int i = 0; i < numProducers; i++){new Thread(new Producer(myQueue)).start();}for(int i = 0; i < numConsumers; i++){new Thread(new Consumer(myQueue)).start();}// Let the simulation run for, say, 10 secondsThread.sleep(10 * 1000);// End of simulation - shut down gracefullySystem.exit(0);}}
При запуске кода вы увидите вывод, аналогичный следующему:
Потребленный ресурс - Размер очереди сейчас = 1Произведенный ресурс - Размер очереди сейчас = 1Потребленный ресурс - Размер очереди сейчас = 1Потребленный ресурс - Размер очереди сейчас = 1Произведенный ресурс - Размер очереди сейчас = 1Произведенный ресурс - Размер очереди сейчас = 1Произведенный ресурс - Размер очереди сейчас = 1Потребление объекта java.lang.Object@14c7f728Потребленный ресурс - Размер очереди сейчас = 0Потребление объекта java.lang.Object@2b71e323Потребленный ресурс - Размер очереди сейчас = 0Произведенный ресурс - Размер очереди сейчас = 0Произведенный ресурс - Размер очереди сейчас = 1Произведенный ресурс - Размер очереди сейчас = 2Потребление объекта java.lang.Object@206dc00bПотребленный ресурс - Размер очереди сейчас = 1Произведенный ресурс - Размер очереди сейчас = 2Произведенный ресурс - Размер очереди сейчас = 3Потребление объекта java.lang.Object@1a000bc0Потребленный ресурс - Размер очереди сейчас = 2Потребление объекта java.lang.Object@25b6183dПотребленный ресурс - Размер очереди сейчас = 1Произведенный ресурс - Размер очереди сейчас = 2Произведенный ресурс - Размер очереди сейчас = 3......Произведенный ресурс - Размер очереди сейчас = 20Потребление объекта java.lang.Object@2b3cd3a6Потребленный ресурс - Размер очереди сейчас = 19Произведенный ресурс - Размер очереди сейчас = 20Потребление объекта java.lang.Object@3876982dПотребленный ресурс - Размер очереди сейчас = 19Произведенный ресурс - Размер очереди сейчас = 20
Вывод ясно показывает, что размер очереди никогда не превышает 20, а потоки-потребители обрабатывают ресурсы очереди, размещенные потоками-производителями. Вот так все просто.
Ссылки: