LinkedBlockingQueue vs ConcurrentLinkedQueue


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

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

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

5 89

5 ответов:

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

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

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

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

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

Java ConcurrentLinkedQueue на алгоритм Магед М. Майкл и Майкл Л. Скотт на без блокировки блокировка-Free очереди.

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

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

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

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

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

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

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

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

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