Приклад SynchronousQueue в Java - рішення задачі Виробник Споживач
SynchronousQueue - це спеціальний тип BlockingQueue, в якому кожна операція insert повинна чекати відповідну команду remove в іншій нитці, і навпаки. Коли ви викликаєте метод put() у SynchronousQueue, він блокується доти, доки інша нитка не забере цей елемент із нього. Відповідно, якщо інша нитка намагається видалити елемент із нього, а елемента там немає, то ця нитка блокується доти, доки інша нитка не покладе елемент у чергу. Можна уявити SynchronousQueue як спортсмена ( нитка ), що біжить з олімпійським факелом, він біжить з факелом (об'єктом який передається) і передає його іншому спортсмену, який очікує з іншого боку. Якщо ви звернете увагу на назву, то зрозумієте, що SynchronousQueue так названо не безпідставно, вінпередає дані синхронізовано в іншу нитку ; він чекає поки хтось забере дані, замість просто покласти їх і завершитися (асинхронна операція). Якщо ви знайомі з CSP та Ada, то ви знаєте, що синхронізовані черги схожі на зустріч потоків. Вони добре підходять для конструкцій передачі управління, в яких об'єкт запущений в одній нитці повинен синхронізуватися з об'єктом в іншій нитці, щоб передати йому якусь інформацію, подію або завдання. У раніше вивчених підручниках з багато-нітевого програмування ми вивчали як вирішити задачу виробник-споживач, використовуючи методи wait і notify , і BlockingQueue . Зараз ми дізнаємося , як застосувати виробник-споживач патерн використовуючи SynchronousQueue.Цей клас додатково підтримує чесну поведінку для впорядкованості очікування ниток виробника та споживача. За умовчанням ця впорядкованість не гарантована. Однак черги, створені з чесними властивостями, роблять гарантованим доступ для ниток у черговості FIFO (Firs In First Out – хто Перший Прийшов, той Перший Вийшов).Виробник споживача використовуючи SynchronousQueue в Java.
Як я говорив вище, немає нічого кращого, ніж завдання виробника-споживача для розуміння між-ниткової взаємодіїу будь-якій мові програмування. У цій проблемі одна нитка виступає як виробник, який виробляє події та завдання, а інша нитка виступає споживачем цього. Загальний буфер використовується передачі даних від виробника до споживача. Складність розв'язання цього завдання приходить у крайніх випадках, наприклад, коли виробник змушений чекати т.к. буфер заповнений чи споживач змушений чекати, т.к. буфер порожній. Це легко вирішувалося, т.к. блокуюча черга надавала як буфер для зберігання даних, а й управління потоком, блокуючи нитку викликає put() метод (Виробник) якщо буфер заповнений, і блокуючи нитку викликає таке() метод (Споживач) якщо буфер порожній. Зараз ми вирішимо це саме завдання використовуючи SynchronousQueue, спеціальний вид паралельних колекційз нульовою ємністю. У наступному прикладі у нас є дві нитки , які називаються PRODUCER і CONSUMER (завжди давайте імена ниткам, це дуже хороший стиль багато-ниткового програмування). Перша нитка розміщує рахунок у грі, а друга нитка його споживає. Рахунок у грі ніщо інше як об'єкт типу String. Але якщо ви запустите програму з іншим типом, ви не помітите жодної різниці. Щоб зрозуміти як SynchronousQueue працює, і як вирішувати завдання виробник-споживач вам потрібно: або запустити програму налагодження (debug) в середовищі Eclipse, або просто запустити нитку виробника закоментувавши consumer.start(); якщо нитка споживача не запущена, то нитка виробника буде заблокована на queue.put(event); якщо запущена, ви не зможете бачити як виробник [PRODUCER] публікує подію :FOUR. Це відбувається т.к. специфічна поведінка SynchronousQueue, яка гарантує, що нитка розміщує дані буде заблокована доти, поки інша нитка не забере ці дані, і навпаки. Ви можете протестувати частину коду, що залишилася, закоментувавши producer.start(); і запускаючи лише нитку споживача. Якщо ви уважно вивчите, що програма виводить, то помітите, що порядок виводу зворотний. Виглядає наче нитка [CONSUMER] забрала дані ще до того, як нитка [PRODUCER] import java.util.concurrent.SynchronousQueue; /** * Java Program to solve Producer Consumer problem using SynchronousQueue. A * call to put() will block until there is a corresponding thread to take() that * element. * * @author Javin Paul */ public class SynchronousQueueDemo{ public static void main(String args[]) { final SynchronousQueue
queue = new SynchronousQueue
(); Thread producer = new Thread("PRODUCER") { public void run() { String event = "FOUR"; try { queue.put(event); // thread will block here System.out.printf("[%s] published event : %s %n", Thread .currentThread() .getName(), event); } catch (InterruptedException e) { e.printStackTrace(); } } }; producer.start(); // starting publisher thread Thread consumer = new Thread("CONSUMER") { public void run() { try { String event = queue.take(); // thread will block here System.out.printf("[%s] consumed event : %s %n", Thread .currentThread() .getName(), event); } catch (InterruptedException e) { e.printStackTrace(); } } }; consumer.start(); // starting consumer thread } } Output: [CONSUMER] consumed event : FOUR [PRODUCER] published event : FOUR
зробила їх. Це сталося через те, що SynchronousQueue за замовчуванням не гарантує черговості. Але вона має правила чесності, які встановлюють доступ до ниток у порядку FIFO. Ви можете включати ці правила передаючи true в перевантажений конструктор SynchronousQueue наприклад: new SynchronousQueue(boolean fair).
Що потрібно запам'ятати про SynchronousQueue в Java.
Тут кілька важливих властивостей цього спеціального типу черги, що блокується в Java. Дуже корисно передавати дані з однієї нитки до іншої синхронізовано. Ця черга не має обсягу і заблокована доти, доки її не звільнить інша нитка.
- SynchronousQueue блокується, і поки одна нитка не буде готова взяти дані, інша буде намагатися покласти дані.
- SynchronousQueue не має обсягу. Тобто, в ній не містяться дані.
- SynchronousQueue використовується для реалізації стратегії черговості прямої передачі управління, де нитка передає управління чекаючою нитки, або створює нову якщо це дозволено, інакше управління не передається.
- Ця черга не пропускає null-дані. Спроба додати елемент null кине NullPointerException .
- Якщо використовувати інші методи з колекції (наприклад, contains), SynchronousQueue поводиться як порожня колекція.
- Ви не зможете використовувати метод peek у SynchronousQueue, тому що елемент існує тільки тоді, коли ви намагаєтеся його видалити; так само ви не зможете вставляти елементи (використовуючи будь-який метод), поки інша нитка не намагається його видалити.
- Ви не зможете використати iterator для SynchronousQueue, т.к. у ній немає елементів.
- SynchronousQueue може створюватися з чесними правилами, коли гарантується доступ до ниток у порядку FIFO.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ