Из JavaDocs:
- ConcurrentLinkedQueue является подходящим выбором , когда много потоков будет общий доступ к общей коллекции. Эта очередь не допускает пустых элементов.
- ArrayBlockingQueue - это классический «ограниченный буфер», в котором массив фиксированного размера содержит элементы, вставленные производителями и извлеченные потребителями. Этот класс поддерживает необязательную политику справедливости для упорядочивания ожидающих потоков производителей и потребителей.
- LinkedBlockingQueue обычно имеет более высокую пропускную способность, чем очереди на основе массивов, но менее предсказуемую производительность в большинстве параллельных приложений.
У меня есть 2 сценария: один требует, чтобы очередь поддерживала множество производителей (потоков, использующих его) с одним потребителем, а другой - наоборот.
Я не понимаю, какую реализацию использовать. Может кто-нибудь объяснить, в чем разница?
Кроме того, что такое «необязательная политика справедливости» в ArrayBlockingQueue
?
java
multithreading
concurrency
queue
Дэвид Хофманн
источник
источник
Ответы:
В основном разница между ними заключается в характеристиках производительности и поведении блокировки.
Самое простое
ArrayBlockingQueue
- это очередь фиксированного размера. Поэтому, если вы установите размер 10 и попытаетесь вставить 11-й элемент, оператор вставки заблокируется, пока другой поток не удалит элемент. Проблема справедливости заключается в том, что происходит, если несколько потоков пытаются одновременно вставлять и удалять (другими словами, в период, когда Очередь была заблокирована). Алгоритм равноправия гарантирует, что первый поток, который запрашивает, будет первым потоком, который получит. В противном случае данный поток может ждать дольше, чем другие потоки, что приводит к непредсказуемому поведению (иногда одному потоку требуется всего несколько секунд, потому что другие потоки, запущенные позже, обрабатываются первыми). Компромисс заключается в том, что для управления справедливостью требуются накладные расходы, что снижает пропускную способность.Наиболее важное различие между
LinkedBlockingQueue
иConcurrentLinkedQueue
заключается в том, что если вы запрашиваете элемент из a,LinkedBlockingQueue
а очередь пуста, ваш поток будет ждать, пока там что-то не будет. A сразуConcurrentLinkedQueue
же вернется с поведением пустой очереди.Какой из них зависит от того, нужна ли вам блокировка. Когда у вас много производителей и один потребитель, это звучит так. С другой стороны, если у вас много потребителей и только один производитель, вам может не понадобиться блокирующее поведение, и вы можете просто попросить потребителей проверить, пуста ли очередь, и продолжить, если это так.
источник
ConcurrentLinkedQueue означает, что блокировки не выполняются (т.е. не выполняются вызовы synchronized (this) или Lock.lock ). Он будет использовать операцию CAS - Compare and Swap во время модификаций, чтобы увидеть, остается ли головной / хвостовой узел таким же, как при запуске. Если это так, операция завершается успешно. Если узел голова / хвост отличается, он будет вращаться и повторить попытку.
LinkedBlockingQueue будет заблокирован перед любым изменением. Таким образом, ваши звонки с предложениями будут блокироваться, пока они не получат блокировку. Вы можете использовать перегрузку предложения, которая требует TimeUnit, чтобы сказать, что вы готовы подождать только X количество времени, прежде чем отказаться от добавления (обычно хорошо для очередей типов сообщений, где сообщение устарело после X количества миллисекунд).
Справедливость означает, что реализация Lock будет поддерживать порядок потоков. Это означает, что если поток A входит, а затем входит поток B, поток A первым получит блокировку. Без всякой справедливости, на самом деле не известно, что происходит. Скорее всего, это будет следующий запланированный поток.
Что касается того, что использовать, это зависит. Я предпочитаю использовать ConcurrentLinkedQueue, потому что время, необходимое моим производителям, чтобы поставить работу в очередь, разнообразно. У меня не так много продюсеров работают одновременно. Но со стороны потребителя сложнее, потому что опрос не переходит в состояние хорошего сна. Вы должны сами справиться с этим.
источник
В заголовке вашего вопроса упоминаются очереди с блокировкой. Однако
ConcurrentLinkedQueue
это не очередь на блокировку.В
BlockingQueue
s являютсяArrayBlockingQueue
,DelayQueue
,LinkedBlockingDeque
,LinkedBlockingQueue
,PriorityBlockingQueue
, иSynchronousQueue
.Некоторые из них явно не подходит для ваших целей (
DelayQueue
,PriorityBlockingQueue
иSynchronousQueue
).LinkedBlockingQueue
иLinkedBlockingDeque
идентичны, за исключением того, что последняя представляет собой двустороннюю очередь (она реализует интерфейс Deque).поскольку
ArrayBlockingQueue
это полезно, только если вы хотите ограничить количество элементов, я бы придерживалсяLinkedBlockingQueue
.источник
ArrayBlockingQueue имеет меньший объем памяти, он может повторно использовать узел элемента, в отличие от LinkedBlockingQueue, который должен создавать объект LinkedBlockingQueue $ Node для каждой новой вставки.
источник
ArrayBlockingQueue
нее будет гораздо худший объем памяти - у нее все еще есть большой массив, выделенный в памяти все время, в то время какLinkedBlockingQueue
будет иметь незначительный объем памяти , когда близко к пустой.SynchronousQueue
(Взято из другого вопроса )SynchronousQueue
больше похоже на передачу обслуживания, тогдаLinkedBlockingQueue
как он позволяет использовать только один элемент. Разница в том, чтоput()
вызов aSynchronousQueue
не вернется, пока не будет соответствующегоtake()
вызова, но сLinkedBlockingQueue
размером 1put()
вызов (в пустую очередь) вернется немедленно. По сути, этоBlockingQueue
реализация, когда вам действительно не нужна очередь (вы не хотите поддерживать какие-либо ожидающие данные).LinkedBlockingQueue
(LinkedList
Реализация, но не совсем JDK. Реализация вLinkedList
нем использует статический внутренний класс Node для поддержания связей между элементами)Конструктор для LinkedBlockingQueue
Класс узла, используемый для поддержки ссылок
3 ArrayBlockingQueue (реализация массива)
Конструктор для ArrayBlockingQueue
IMHO Самая большая разница между
ArrayBlockingQueue
иLinkedBlockingQueue
очевидна из конструктора, у которого есть основная структура данных Array и другой связанныйList .ArrayBlockingQueue
использует алгоритм двойного условия одиночной блокировки иLinkedBlockingQueue
является вариантом алгоритма «очереди с двумя блокировками» и имеет 2 блокировки 2 условия (takeLock, putLock)источник
ConcurrentLinkedQueue не блокируется, а LinkedBlockingQueue - нет. Каждый раз, когда вы вызываете LinkedBlockingQueue.put () или LinkedBlockingQueue.take (), вам нужно сначала получить блокировку. Другими словами, у LinkedBlockingQueue плохой параллелизм. Если вам важна производительность, попробуйте ConcurrentLinkedQueue + LockSupport.
источник