Ma question porte sur cette question posée plus tôt. Dans les situations où j'utilise une file d'attente pour la communication entre les threads producteur et consommateur, les gens recommandent-ils généralement d'utiliser LinkedBlockingQueue
ou ConcurrentLinkedQueue
?
Quels sont les avantages / inconvénients de l'utilisation de l'un par rapport à l'autre?
La principale différence que je peux voir du point de vue de l'API est que a LinkedBlockingQueue
peut être facultativement délimité.
ConcurrentLinkedQueue
est également utile si votre thread vérifie plusieurs files d'attente. Par exemple, dans un serveur multi-tenant. En supposant que pour des raisons d'isolement, vous n'utilisez pas une seule file d'attente de blocage et un discriminateur de locataire à la place.take()
etput()
consomme simplement des ressources supplémentaires (en termes de synchronisation) queConcurrentLinkedQueue
. bien qu'il soit le cas d'utiliser des files d'attente limitées pour les scénarios producteur-consommateurCette question mérite une meilleure réponse.
Java
ConcurrentLinkedQueue
est basé sur le célèbre algorithme de Maged M. Michael et Michael L. Scott pour les files d' attente non bloquantes sans verrouillage .«Non bloquant» comme terme ici pour une ressource contestée (notre file d'attente) signifie que indépendamment de ce que fait le planificateur de la plate-forme, comme interrompre un thread, ou si le thread en question est tout simplement trop lent, d'autres threads se disputent la même ressource pourra encore progresser. Si un verrou est impliqué par exemple, le thread contenant le verrou pourrait être interrompu et tous les threads en attente de ce verrou seraient bloqués. Les verrous intrinsèques (le
synchronized
mot - clé) en Java peuvent également entraîner une pénalité sévère pour les performances - comme lors d' un verrouillage biaiséest impliqué et vous avez un conflit, ou après que la machine virtuelle a décidé de "gonfler" le verrou après une période de grâce de rotation et de bloquer les threads en conflit ... c'est pourquoi dans de nombreux contextes (scénarios de conflit faible / moyen), faire comparer et -sets sur des références atomiques peuvent être beaucoup plus efficaces et c'est exactement ce que font de nombreuses structures de données non bloquantes.Java
ConcurrentLinkedQueue
n'est pas seulement non bloquant, mais il a la propriété impressionnante que le producteur ne lutte pas avec le consommateur. Dans un scénario producteur / consommateur unique (SPSC), cela signifie vraiment qu'il n'y aura pas de conflit à proprement parler. Dans un scénario de producteur multiple / consommateur unique, le consommateur ne sera pas en conflit avec les producteurs. Cette file d'attente a un conflit lorsque plusieurs producteurs essaient de le faireoffer()
, mais c'est la concurrence par définition. C'est essentiellement une file d'attente non bloquante à usage général et efficace.Quant au fait que ce n'est pas un
BlockingQueue
, eh bien, bloquer un thread pour attendre dans une file d'attente est une façon terriblement terrible de concevoir des systèmes concurrents. Ne fais pas ça. Si vous ne pouvez pas comprendre comment utiliser unConcurrentLinkedQueue
dans un scénario consommateur / producteur, passez simplement à des abstractions de niveau supérieur, comme un bon framework d'acteur.la source
LinkedBlockingQueue
bloque le consommateur ou le producteur lorsque la file d'attente est vide ou pleine et que le thread consommateur / producteur respectif est mis en veille. Mais cette fonction de blocage a un coût: chaque opération de vente ou de prise est un verrou disputé entre les producteurs ou les consommateurs (le cas échéant), de sorte que dans les scénarios avec de nombreux producteurs / consommateurs, l'opération peut être plus lente.ConcurrentLinkedQueue
n'utilise pas de verrous, mais CAS , sur ses opérations put / take réduisant potentiellement les conflits avec de nombreux threads producteurs et consommateurs. Mais étant une structure de données « attendre libre »,ConcurrentLinkedQueue
ne bloque pas quand il est vide, ce qui signifie que le consommateur devra faire face auxtake()
retour desnull
valeurs par « attente occupé », par exemple, avec le fil de consommation dévorant CPU.Donc, lequel est "meilleur" dépend du nombre de threads consommateurs, du taux de consommation / production, etc. Un benchmark est nécessaire pour chaque scénario.
Un cas d'utilisation particulier où le
ConcurrentLinkedQueue
est clairement meilleur est lorsque les producteurs produisent d'abord quelque chose et terminent leur travail en plaçant le travail dans la file d'attente et seulement après que les consommateurs commencent à consommer, sachant qu'ils seront terminés lorsque la file d'attente est vide. (ici il n'y a pas de concurrence entre producteur-consommateur mais seulement entre producteur-producteur et consommateur-consommateur)la source
unbounded blockingqueue
est utilisé, serait-il meilleur que le concurrent basé sur CASConcurrentLinkedQueue
Une autre solution (qui ne s'adapte pas bien) est les canaux de rendez-vous: java.util.concurrent SynchronousQueue
la source
Si votre file d'attente n'est pas extensible et ne contient qu'un seul thread producteur / consommateur. Vous pouvez utiliser la file d'attente sans verrouillage (vous n'avez pas besoin de verrouiller l'accès aux données).
la source