LinkedBlockingQueue против ConcurrentLinkedQueue - PullRequest
99 голосов
/ 15 сентября 2009

Мой вопрос относится к этому вопросу , заданному ранее. В ситуациях, когда я использую очередь для связи между производителями и потребителями, люди обычно рекомендуют использовать LinkedBlockingQueue или ConcurrentLinkedQueue?

Каковы преимущества / недостатки использования одного над другим?

Основное отличие, которое я вижу с точки зрения API, заключается в том, что LinkedBlockingQueue может быть ограничено.

Ответы [ 5 ]

98 голосов
/ 15 сентября 2009

Для потока производителя / потребителя я не уверен, что ConcurrentLinkedQueue - даже разумный вариант - он не реализует BlockingQueue, который является основным интерфейсом для очередей IMO производителя / потребителя. Вам нужно было бы позвонить poll(), немного подождать, если вы ничего не нашли, а затем провести повторный опрос и т. Д., Что приведет к задержкам, когда появляется новый элемент, и неэффективности, когда он пуст (из-за ненужного пробуждения от сна).

Из документов для BlockingQueue:

BlockingQueue реализации предназначены для использования в основном для очередей производитель-потребитель

Я знаю, что не строго говорит, что для очередей производитель-потребитель должны использоваться только блокирующие очереди, но даже в этом случае ...

60 голосов
/ 27 июня 2014

Этот вопрос заслуживает лучшего ответа.

Java ConcurrentLinkedQueue основан на знаменитом алгоритме Магеда М. Майкла и Майкла Л. Скотта для неблокирующих без блокировки очередей.

«Неблокирующая» в качестве термина для конкурирующего ресурса (нашей очереди) означает, что независимо от того, что делает планировщик платформы, например прерывание потока или если рассматриваемый поток просто слишком медленный, другие потоки борются за тот же ресурс все еще сможет прогрессировать. Например, если включена блокировка, поток, удерживающий блокировку, может быть прерван, и все потоки, ожидающие эту блокировку, будут заблокированы. Внутренние блокировки (ключевое слово synchronized) в Java также могут привести к серьезным потерям производительности - например, когда задействована смещенная блокировка , и у вас есть конфликт или после того, как виртуальная машина решит «накачать» блокировку после периода отсрочки вращения и блокирующих конкурирующих потоков ... именно поэтому во многих контекстах (сценарии низкой / средней конкуренции) выполнение сравнения и установки на атомарных ссылках может быть намного более эффективным, и это именно то, что многие неблокирующие структуры данных делают.

Java ConcurrentLinkedQueue не только неблокирует, но и обладает удивительным свойством, с которым производитель не борется с потребителем. В сценарии с одним производителем / одним потребителем (SPSC) это действительно означает, что не будет никаких разногласий. В сценарии с несколькими производителями / одним потребителем потребитель не будет бороться с производителями. Эта очередь имеет конфликт, когда несколько производителей пытаются offer(), но это по определению параллелизм. Это в основном универсальная и эффективная неблокирующая очередь.

Что касается того, что это не BlockingQueue, ну, блокировка потока для ожидания в очереди - это странно ужасный способ проектирования параллельных систем. Не. Если вы не можете понять, как использовать ConcurrentLinkedQueue в сценарии «потребитель / производитель», просто переключитесь на абстракции более высокого уровня, такие как хорошая структура актера.

24 голосов
/ 13 февраля 2014

LinkedBlockingQueue блокирует потребителя или производителя, когда очередь пуста или заполнена, и соответствующий поток потребителя / производителя переводится в спящий режим. Но эта блокирующая функция имеет свою цену: каждая операция «положить или взять» является блокировкой между производителями или потребителями (если их много), поэтому в сценариях со многими производителями / потребителями операция может быть медленнее.

ConcurrentLinkedQueue не использует блокировки, но CAS для операций ввода / вывода, что потенциально снижает конкуренцию со многими потоками производителей и потребителей. Но, будучи структурой данных «без ожидания», ConcurrentLinkedQueue не будет блокироваться, когда пусто, а это означает, что потребителю придется иметь дело с take(), возвращающими значения null с помощью «занятого ожидания», например, с потоком потребителя. поедает процессор.

То, какой из них «лучше», зависит от количества потоков потребителей, от скорости, которую они потребляют / производят, и т. Д. Для каждого сценария необходим эталон.

Один конкретный случай использования, где ConcurrentLinkedQueue явно лучше, - это когда производители сначала что-то производят и заканчивают свою работу, помещая работу в очередь , и только после потребители начинают потреблять, зная, что они будет сделано, когда очередь пуста. (здесь нет параллелизма между производителем-потребителем, а только между производителем-производителем и потребителем-потребителем)

1 голос
/ 15 сентября 2009

Другое решение (которое плохо масштабируется) - каналы рандеву: java.util.concurrent SynchronousQueue

0 голосов
/ 15 сентября 2009

Если ваша очередь не расширяемая и содержит только один поток производителя / потребителя. Вы можете использовать очередь без блокировки (вам не нужно блокировать доступ к данным).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...