Мой вопрос относится к заданному ранее вопросу . В ситуациях, когда я использую очередь для связи между потоками производителя и потребителя, люди обычно рекомендуют использовать LinkedBlockingQueue
или ConcurrentLinkedQueue
?
Каковы преимущества / недостатки использования одного над другим?
Основное отличие, которое я вижу с точки зрения API, заключается в том, что a LinkedBlockingQueue
может быть дополнительно ограничен.
ConcurrentLinkedQueue
также полезен, если ваш поток проверяет несколько очередей. Например, на мультитенантном сервере. Предполагая, что по причинам изоляции вы не используете вместо этого одну блокирующую очередь и дискриминатор клиента.take()
иput()
просто потребляем дополнительный ресурс (в смысле синхронизации), чемConcurrentLinkedQueue
. хотя это случай использования ограниченных очередей для сценариев производитель-потребительЭтот вопрос заслуживает лучшего ответа.
Java
ConcurrentLinkedQueue
основан на известном алгоритме, разработанном Магедом М. Майклом и Майклом Л. Скоттом для неблокирующей и свободной от блокировки очередей.«Неблокирующий» здесь термин для конкурирующего ресурса (наша очередь) означает, что независимо от того, что делает планировщик платформы, например, прерывает поток, или если рассматриваемый поток просто слишком медленный, другие потоки конкурируют за тот же ресурс все еще сможет прогрессировать. Если, например, задействована блокировка, поток, удерживающий блокировку, может быть прерван, и все потоки, ожидающие этой блокировки, будут заблокированы. Внутренние блокировки (
synchronized
ключевое слово) в Java также могут иметь серьезные последствия для производительности - например, при смещенной блокировке.вовлечен, и у вас действительно есть конкуренция, или после того, как виртуальная машина решит «раздуть» блокировку после периода отсрочки вращения и заблокировать конкурирующие потоки ... вот почему во многих контекстах (сценарии низкой / средней конкуренции) выполняется сравнение и -наборы по атомарным ссылкам могут быть гораздо более эффективными, и это именно то, что делают многие неблокирующие структуры данных.Java
ConcurrentLinkedQueue
не только не блокирует, но и обладает замечательным свойством, которое производитель не борется с потребителем. В сценарии с одним производителем и одним потребителем (SPSC) это действительно означает, что не будет никаких разногласий. В сценарии с несколькими производителями и одним потребителем потребитель не будет бороться с производителями. Эта очередь действительно имеет конкуренцию, когда несколько производителей пытаются сделать этоoffer()
, но это по определению параллелизм. Это в основном универсальная и эффективная неблокирующая очередь.Что касается того, что это не ...
BlockingQueue
ну, блокирование потока для ожидания в очереди - это ужасно ужасный способ проектирования параллельных систем. Не. Если вы не можете понять, как использовать aConcurrentLinkedQueue
в сценарии потребителя / производителя, просто переключитесь на абстракции более высокого уровня, например, на хорошую структуру акторов.источник
LinkedBlockingQueue
блокирует потребителя или производителя, когда очередь пуста или заполнена, а соответствующий поток потребителя / производителя переводится в спящий режим. Но за эту функцию блокировки приходится платить: каждая операция вставки или приема является блокировкой, конкурирующей между производителями или потребителями (если их много), поэтому в сценариях с большим количеством производителей / потребителей операция может быть медленнее.ConcurrentLinkedQueue
не использует блокировки, а использует CAS для операций ввода / вывода, что потенциально снижает конкуренцию со многими потоками производителей и потребителей. Но будучи структурой данных «без ожидания»,ConcurrentLinkedQueue
она не будет блокироваться, когда она пуста, а это означает, что потребителю придется иметь дело сtake()
возвращаемымиnull
значениями путем «ожидания занятости», например, когда поток потребителя поглощает процессор.Итак, какой из них «лучше», зависит от количества потоков-потребителей, от скорости их потребления / производства и т. Д. Для каждого сценария требуется эталонный тест.
Один конкретный вариант использования, где
ConcurrentLinkedQueue
явно лучше, - это когда производители сначала что-то производят и завершают свою работу, помещая работу в очередь, и только после того, как потребители начинают потреблять, зная, что они будут выполнены, когда очередь пуста. (здесь нет параллелизма между производителем-потребителем, а только между производителем-производителем и потребителем-потребителем)источник
unbounded blockingqueue
будет использоваться, будет ли он лучше, чем параллельныйConcurrentLinkedQueue
Другое решение (которое плохо масштабируется) - это каналы рандеву: java.util.concurrent SynchronousQueue
источник
Если ваша очередь не расширяется и содержит только один поток производителя / потребителя. Вы можете использовать очередь без блокировки (вам не нужно блокировать доступ к данным).
источник