LinkedBlockingQueue vs ConcurrentLinkedQueue

Ma question concerne cette question posée plus tôt. Dans les situations où j’utilise une queue pour la communication entre les threads de production et de consommation, est-ce que les gens recommandent généralement d’utiliser LinkedBlockingQueue ou ConcurrentLinkedQueue ?

Quels sont les avantages / inconvénients de l’utilisation l’un par rapport à l’autre?

La principale différence que je peux voir du sharepoint vue de l’API est qu’une LinkedBlockingQueue peut éventuellement être bornée.

Pour un thread producteur / consommateur, je ne suis pas sûr que ConcurrentLinkedQueue soit une option raisonnable – il n’implémente pas BlockingQueue , qui est l’interface fondamentale pour les files d’attente producteur / consommateur IMO. Vous devrez appeler poll() , attendre un peu si vous n’avez rien trouvé, puis interroger à nouveau, etc., entraînant des retards quand un nouvel élément arrive et des problèmes d’inefficacité (à cause d’un réveil inutile) de dormir).

Depuis la documentation de BlockingQueue:

BlockingQueue implémentations de BlockingQueue sont conçues pour être utilisées principalement pour les files d’attente producteur-consommateur

Je sais qu’il ne dit pas ssortingctement que seules les files d’attente bloquantes doivent être utilisées pour les files d’attente entre producteurs et consommateurs, mais malgré tout …

Cette question mérite une meilleure réponse.

Le ConcurrentLinkedQueue de Java est basé sur le célèbre algorithme de Maged M. Michael et Michael L. Scott pour les files d’ attente sans blocage .

“Non-blocage” en tant que terme ici pour une ressource contestée (notre queue) signifie que quel que soit le planificateur de la plate-forme, comme interrompre un thread, ou si le thread en question est simplement trop lent, d’autres threads sera toujours en mesure de progresser. Si un verrou est impliqué par exemple, le thread qui maintient le verrou peut être interrompu et tous les threads en attente de ce verrou seront bloqués. Les verrous insortingnsèques (le mot clé synchronized ) en Java peuvent également entraîner une pénalité sévère pour les performances – par exemple, lorsqu’un locking biaisé est impliqué et que vous avez des conflits, ou après que la machine virtuelle décide de gonfler le verrou après une période de grâce threads … c’est pourquoi dans de nombreux contextes (scénarios de contention faible / moyenne), faire des comparaisons et des ensembles sur des références atomiques peut être beaucoup plus efficace et c’est exactement ce que font de nombreuses structures de données non bloquantes.

Le ConcurrentLinkedQueue de Java n’est pas seulement non bloquant, mais il possède la propriété impressionnante que le producteur ne doit pas affronter avec le consommateur. Dans un scénario producteur unique / consommateur unique (SPSC), cela signifie qu’il n’y aura pas de conflit. Dans un scénario à plusieurs producteurs / consommateurs individuels, le consommateur ne sera pas confronté aux producteurs. Cette queue a des conflits lorsque plusieurs producteurs tentent de offer() , mais c’est la concurrence par définition. Il s’agit essentiellement d’une queue non bloquante à usage général et efficace.

En ce qui concerne le fait qu’il ne s’agisse pas d’une BlockingQueue , le blocage d’un thread dans une file d’attente est un moyen effrayant de concevoir des systèmes concurrents. Ne pas Si vous ne savez pas comment utiliser un ConcurrentLinkedQueue dans un scénario consommateur / producteur, passez simplement à des abstractions de niveau supérieur, comme une bonne structure d’acteurs.

LinkedBlockingQueue bloque le consommateur ou le producteur lorsque la queue est vide ou pleine et que le thread consommateur / producteur correspondant est mis en veille. Mais cette fonctionnalité de blocage a un coût: chaque opération de mise ou de prise est soumise à un conflit entre les producteurs ou les consommateurs (si tant est qu’il y en a plusieurs).

ConcurrentLinkedQueue n’utilise pas de verrous, mais CAS , sur ses opérations put / take, réduit potentiellement les conflits avec de nombreux threads producteurs et consommateurs. Mais en tant que structure de données “sans attente”, ConcurrentLinkedQueue ne bloquera pas lorsqu’il est vide, ce qui signifie que le consommateur devra traiter les valeurs null take() avec “busy attente”, par exemple, le thread consommateur consommant le CPU.

Ainsi, celui qui est “meilleur” dépend du nombre de sujets de consommation, du taux de consommation / production, etc. Un scénario est nécessaire pour chaque scénario.

Un cas d’utilisation particulier où ConcurrentLinkedQueue est nettement meilleur est lorsque les producteurs produisent d’abord quelque chose et finissent leur travail en plaçant le travail dans la queue et seulement après que les consommateurs commencent à consumr, sachant qu’ils le seront lorsque la queue est vide. (il n’y a pas de concurrence entre producteur-consommateur mais seulement entre producteur-producteur et consommateur-consommateur)

Une autre solution (qui ne s’adapte pas bien) est les canaux de rendez-vous: java.util.concurrent SynchronousQueue

Si votre queue n’est pas extensible et ne contient qu’un seul thread producteur / consommateur. Vous pouvez utiliser une queue sans locking (vous n’avez pas besoin de verrouiller l’access aux données).