Question

Ma question concerne cette question demandé plus tôt. Dans les cas où j'utilise une file d'attente pour la communication entre les threads producteurs et consommateurs, les utilisateurs recommandent-ils généralement d'utiliser LinkedBlockingQueue ou ConcurrentLinkedQueue ?

Quels sont les avantages / inconvénients d’utiliser l’un sur l’autre?

La principale différence que je peux voir du point de vue de l'API est qu'un LinkedBlockingQueue peut être lié de manière facultative.

Était-ce utile?

La solution

Pour un thread producteur / consommateur, je ne suis pas sûr que ConcurrentLinkedQueue soit même une option raisonnable - il ne met pas en œuvre BlockingQueue , qui est l'interface fondamentale pour files d'attente de producteurs / consommateurs IMO. Vous devez appeler poll () , attendre un peu si vous n’avez rien trouvé, puis interroger à nouveau, etc., ce qui entraîne des retards lorsqu’un nouvel élément arrive et des inefficacités quand il est vide (à cause du réveil inutile du sommeil).

À partir de la documentation pour BlockingQueue:

  Les implémentations

BlockingQueue sont conçues pour être utilisées principalement pour les files d'attente producteur-consommateur

Je sais que cela ne

signifie pas strictement que seules les files d'attente bloquantes devraient être utilisées pour les files d'attente producteur-consommateur, mais malgré tout ...

Autres conseils

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 files d'attente sans blocage non bloquantes.

" Non bloquant " terme ici pour une ressource en conflit (notre file d'attente) signifie que, quel que soit le planificateur de la plate-forme, comme interrompre un fil, ou si le fil en question est tout simplement trop lent, d'autres threads en lice pour la même ressource pourront toujours le progrès. Si un verrou est impliqué par exemple, le thread qui le tient pourrait être interrompu et tous les threads en attente de ce verrou seraient bloqués. Les verrous intrinsèques (le mot clé synchronized ) en Java peuvent également entraîner de lourdes pénalités en termes de performances, comme lorsque Le verrouillage biaisé est impliqué et vous avez des conflits, ou après que la machine virtuelle a décidé de" gonfler ". le verrou après une période de rotation et le blocage des threads en conflit ... C'est pourquoi, dans de nombreux contextes (scénarios de contention faible / moyenne), comparer et définir des références atomiques peut être beaucoup plus efficace et c'est exactement ce que beaucoup de non -blocage de structures de données en cours.

Le ConcurrentLinkedQueue de Java est non seulement non bloquant, mais il a la propriété géniale que le producteur ne lutte pas avec le consommateur. Dans un scénario producteur / consommateur unique (SPSC), cela signifie réellement qu'il n'y aura pas de conflit à proprement parler. Dans un scénario multi producteur / consommateur unique, le consommateur ne fera pas concurrence aux producteurs. Cette file d'attente est en conflit lorsque plusieurs producteurs tentent de offer () , mais il s'agit d'une concurrence d'accès simultané par définition. Il s’agit essentiellement d’une file d’attente non bloquante à usage général et efficace.

Pour ce qui est de ne pas être un BlockingQueue , le fait d'empêcher un thread d'attendre une file d'attente est un moyen terriblement terrible 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 un bon framework d'acteurs.

LinkedBlockingQueue bloque le consommateur ou le producteur lorsque la file d'attente 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 d'achat ou de vente est interdite aux producteurs ou aux 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 être un "attendre gratuitement" La structure de données, ConcurrentLinkedQueue ne se bloque pas lorsqu'elle est vide, ce qui signifie que le consommateur devra gérer les take () renvoyant les valeurs null par " occupé en attente " ;, par exemple, avec le fil consommateur dévorant le processeur.

Alors lequel est le "meilleur" dépend du nombre de threads grand public, du taux de consommation / production qu'ils produisent, etc. Un repère est nécessaire pour chaque scénario.

Un cas d'utilisation particulier où ConcurrentLinkedQueue est nettement préférable consiste à commencer par produire quelque chose et à terminer son travail en plaçant le travail dans la file d'attente et seulement après que le consommateur commence à consommer, sachant qu'ils seront terminés lorsque la file d'attente 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 celle des canaux de rendez-vous: java.util.concurrent SynchronousQueue

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).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top