No JavaDocs:
- Um ConcurrentLinkedQueue é uma escolha apropriada quando muitos threads compartilham o acesso a uma coleção comum. Essa fila não permite elementos nulos.
- ArrayBlockingQueue é um "buffer limitado" clássico, no qual uma matriz de tamanho fixo mantém elementos inseridos pelos produtores e extraídos pelos consumidores. Esta classe suporta uma política de justiça opcional para solicitar threads de produtor e consumidor em espera
- O LinkedBlockingQueue normalmente tem uma taxa de transferência mais alta que as filas baseadas em matriz, mas desempenho menos previsível na maioria dos aplicativos simultâneos.
Eu tenho 2 cenários, um requer a fila para oferecer suporte a muitos produtores (threads usando) com um consumidor e o outro é o contrário.
Não entendo qual implementação usar. Alguém pode explicar quais são as diferenças?
Além disso, qual é a 'política de justiça opcional' na ArrayBlockingQueue
?
java
multithreading
concurrency
queue
David Hofmann
fonte
fonte
Respostas:
Basicamente, a diferença entre eles são características de desempenho e comportamento de bloqueio.
Tomando o mais fácil primeiro,
ArrayBlockingQueue
é uma fila de tamanho fixo. Portanto, se você definir o tamanho em 10 e tentar inserir um 11º elemento, a instrução insert será bloqueada até que outro thread remova um elemento. O problema da justiça é o que acontece se vários encadeamentos tentarem inserir e remover ao mesmo tempo (em outras palavras, durante o período em que a Fila foi bloqueada). Um algoritmo de justiça garante que o primeiro thread que solicita seja o primeiro que recebe. Caso contrário, um determinado encadeamento poderá esperar mais tempo do que outros, causando um comportamento imprevisível (algumas vezes, um encadeamento levará apenas alguns segundos porque outros encadeamentos iniciados posteriormente foram processados primeiro). A desvantagem é que é preciso sobrecarga para gerenciar a justiça, diminuindo a produtividade.A diferença mais importante entre
LinkedBlockingQueue
eConcurrentLinkedQueue
é que, se você solicitar um elemento deLinkedBlockingQueue
ae a fila estiver vazia, seu encadeamento aguardará até que exista algo lá. AConcurrentLinkedQueue
retornará imediatamente com o comportamento de uma fila vazia.Qual deles depende se você precisar do bloqueio. Onde você tem muitos produtores e um consumidor, parece que sim. Por outro lado, onde você tem muitos consumidores e apenas um produtor, pode não precisar do comportamento de bloqueio e pode ficar feliz em pedir que os consumidores verifiquem se a fila está vazia e siga em frente.
fonte
ConcurrentLinkedQueue significa que nenhum bloqueio é realizado (ou seja, nenhuma chamada sincronizada (isso) ou Lock.lock ). Ele utilizará uma operação CAS - Compare e Swap durante as modificações para verificar se o nó principal / final ainda é o mesmo de quando foi iniciado. Nesse caso, a operação é bem-sucedida. Se o nó da cabeça / cauda for diferente, ele girará e tentará novamente.
LinkedBlockingQueue terá um bloqueio antes de qualquer modificação. Portanto, suas chamadas de oferta serão bloqueadas até que elas atinjam o bloqueio. Você pode usar a sobrecarga de oferta que leva um TimeUnit para dizer que só deseja esperar X tempo antes de abandonar a adição (geralmente bom para filas de tipo de mensagem em que a mensagem é obsoleta após um número X de milissegundos).
Justiça significa que a implementação do bloqueio manterá os encadeamentos ordenados. Ou seja, se o Thread A entrar e depois o Thread B, o Thread A obterá o bloqueio primeiro. Sem justiça, é indefinido o que realmente acontece. Provavelmente será o próximo segmento agendado.
Quanto a qual usar, depende. Costumo usar ConcurrentLinkedQueue porque o tempo que meus produtores levam para colocar trabalho na fila é diverso. Não tenho muitos produtores produzindo exatamente no mesmo momento. Mas o lado do consumidor é mais complicado porque a pesquisa não entra em bom estado de sono. Você tem que lidar com isso sozinho.
fonte
O título da sua pergunta menciona Bloquear filas. No entanto, não
ConcurrentLinkedQueue
é uma fila de bloqueio.Os
BlockingQueue
s sãoArrayBlockingQueue
,DelayQueue
,LinkedBlockingDeque
,LinkedBlockingQueue
,PriorityBlockingQueue
, eSynchronousQueue
.Alguns destes não são claramente apto para a sua finalidade (
DelayQueue
,PriorityBlockingQueue
, eSynchronousQueue
).LinkedBlockingQueue
eLinkedBlockingDeque
são idênticos, exceto que o último é uma Fila de extremidade dupla (implementa a interface Deque).Como
ArrayBlockingQueue
só é útil se você quiser limitar o número de elementos, eu continuariaLinkedBlockingQueue
.fonte
ArrayBlockingQueue tem menor espaço de memória, pode reutilizar o nó do elemento, não como o LinkedBlockingQueue, que precisa criar um objeto LinkedBlockingQueue $ Node para cada nova inserção.
fonte
ArrayBlockingQueue
para memória será muito pior - ainda haverá uma grande matriz alocada na memória o tempo todo, enquanto o espaçoLinkedBlockingQueue
terá memória insignificante quando estiver quase vazio.SynchronousQueue
(Retirado de outra pergunta )SynchronousQueue
é mais uma transferência, enquanto oLinkedBlockingQueue
just permite um único elemento. A diferença é que aput()
chamada para aSynchronousQueue
não retornará até que haja umatake()
chamada correspondente , mas comLinkedBlockingQueue
tamanho 1, aput()
chamada (para uma fila vazia) retornará imediatamente. É essencialmente aBlockingQueue
implementação para quando você realmente não deseja uma fila (não deseja manter nenhum dado pendente).LinkedBlockingQueue
(LinkedList
Implementação, mas não exatamente a implementação do JDK)LinkedList
Ele usa o nó estático da classe interna para manter os links entre os elementos)Construtor para LinkedBlockingQueue
Classe de nó usada para manter links
3) ArrayBlockingQueue (Implementação de matriz)
Construtor para ArrayBlockingQueue
Maior diferença entre IMHO
ArrayBlockingQueue
eLinkedBlockingQueue
é clara a partir do construtor um tem matriz de estrutura de dados subjacente e outro linkedList .ArrayBlockingQueue
usa o algoritmo de condição dupla de bloqueio único eLinkedBlockingQueue
é uma variante do algoritmo "fila de dois bloqueios" e possui 2 condições de 2 bloqueios (takeLock, putLock)fonte
ConcurrentLinkedQueue não tem bloqueio, LinkedBlockingQueue não. Toda vez que você chama o LinkedBlockingQueue.put () ou o LinkedBlockingQueue.take (), é necessário adquirir o bloqueio primeiro. Em outras palavras, o LinkedBlockingQueue tem baixa concorrência. Se você gosta de desempenho, tente ConcurrentLinkedQueue + LockSupport.
fonte