Minha pergunta se refere a essa pergunta feita anteriormente. Em situações em que estou usando uma fila para comunicação entre os encadeamentos do produtor e do consumidor, as pessoas geralmente recomendam o uso de LinkedBlockingQueue
ou ConcurrentLinkedQueue
?
Quais são as vantagens / desvantagens de usar um em relação ao outro?
A principal diferença que posso ver da perspectiva da API é que a LinkedBlockingQueue
pode ser opcionalmente limitada.
ConcurrentLinkedQueue
também é útil se sua thread estiver verificando várias filas. Por exemplo, em um servidor multilocatário. Supondo que, por motivos de isolamento, você não use uma única fila de bloqueio e um discriminador de inquilino.take()
eput()
apenas consumir recursos extras (interms de sincronização) do queConcurrentLinkedQueue
. embora seja o caso de usar filas limitadas para cenários produtor-consumidorEsta pergunta merece uma resposta melhor.
Java
ConcurrentLinkedQueue
é baseado no famoso algoritmo de Maged M. Michael e Michael L. Scott para bloqueio sem bloqueio sem bloqueio filas ."Sem bloqueio" como um termo aqui para um recurso em disputa (nossa fila) significa que, independentemente do que o agendador da plataforma faz, como interromper um thread ou se o thread em questão é simplesmente muito lento, outros threads disputam o mesmo recurso ainda será capaz de progredir. Se houver um bloqueio envolvido, por exemplo, o encadeamento que o contém pode ser interrompido e todos os encadeamentos que aguardam esse bloqueio serão bloqueados. Bloqueios intrínsecos (o
synchronized
palavra chave) em Java também podem vir com uma grande penalidade para o desempenho - como quando o bloqueio tendenciosoestá envolvido e você tem contenção, ou depois que a VM decide "aumentar" o bloqueio após um período de carência de giro e bloquear threads em conflito ... é por isso que em muitos contextos (cenários de contenção baixa / média), fazendo comparação e -sets em referências atômicas podem ser muito mais eficientes e isso é exatamente o que muitas estruturas de dados sem bloqueio estão fazendo.O Java
ConcurrentLinkedQueue
não só não bloqueia, mas também tem a propriedade incrível de que o produtor não contende com o consumidor. Em um cenário de produtor / consumidor único (SPSC), isso realmente significa que não haverá contenção. Em um cenário de produtor múltiplo / consumidor único, o consumidor não contenderá com os produtores. Essa fila tem contenção quando vários produtores tentamoffer()
, mas isso é simultaneidade por definição. É basicamente uma fila sem bloqueio de uso geral e eficiente.Por não ser um
BlockingQueue
, bem, bloquear um thread para esperar em uma fila é uma forma terrivelmente terrível de projetar sistemas simultâneos. Não. Se você não consegue descobrir como usar umConcurrentLinkedQueue
em um cenário de consumidor / produtor, apenas mude para abstrações de nível superior, como uma boa estrutura de ator.fonte
LinkedBlockingQueue
bloqueia o consumidor ou o produtor quando a fila está vazia ou cheia e o respectivo segmento de consumidor / produtor é colocado em hibernação. Mas esse recurso de bloqueio tem um custo: cada operação de colocação ou retirada é disputada por bloqueio entre os produtores ou consumidores (se houver), portanto, em cenários com muitos produtores / consumidores, a operação pode ser mais lenta.ConcurrentLinkedQueue
não está usando bloqueios, mas CAS , em suas operações put / take potencialmente reduzindo a contenção com muitos encadeamentos de produtor e consumidor. Porém, sendo uma estrutura de dados "livre de espera",ConcurrentLinkedQueue
não será bloqueada quando vazia, o que significa que o consumidor precisará lidar com os valorestake()
retornadosnull
por "espera ocupada", por exemplo, com a thread do consumidor consumindo CPU.Então, qual é "melhor" depende do número de threads de consumo, da taxa que eles consomem / produzem, etc. Um benchmark é necessário para cada cenário.
Um caso de uso específico em que o
ConcurrentLinkedQueue
é claramente melhor é quando os produtores primeiro produzem algo e terminam seu trabalho, colocando o trabalho na fila, e somente depois que os consumidores começam a consumir, sabendo que o farão quando a fila estiver vazia. (aqui não há concorrência entre produtor-consumidor, mas apenas entre produtor-produtor e consumidor-consumidor)fonte
unbounded blockingqueue
for usado, seria melhor do que o concorrente baseado em CASConcurrentLinkedQueue
Outra solução (que não se adapta bem) são os canais de rendezvous: java.util.concurrent SynchronousQueue
fonte
Se sua fila não for expansível e contiver apenas um encadeamento produtor / consumidor. Você pode usar a fila sem bloqueio (você não precisa bloquear o acesso aos dados).
fonte