À partir des JavaDocs:
- Un ConcurrentLinkedQueue est un choix approprié lorsque de nombreux threads partageront l'accès à une collection commune. Cette file d'attente n'autorise pas les éléments nuls.
- ArrayBlockingQueue est un "tampon borné" classique, dans lequel un tableau de taille fixe contient des éléments insérés par les producteurs et extraits par les consommateurs. Cette classe prend en charge une stratégie d'équité facultative pour la commande des threads producteurs et consommateurs en attente
- LinkedBlockingQueue a généralement un débit plus élevé que les files d'attente basées sur un tableau, mais des performances moins prévisibles dans la plupart des applications simultanées.
J'ai 2 scénarios, l'un nécessite que la file d'attente prenne en charge de nombreux producteurs (threads l'utilisant) avec un consommateur et l'autre est l'inverse.
Je ne comprends pas quelle implémentation utiliser. Quelqu'un peut-il expliquer quelles sont les différences?
Aussi, quelle est la «politique d'équité facultative» dans le ArrayBlockingQueue
?
java
multithreading
concurrency
queue
David Hofmann
la source
la source
Réponses:
Fondamentalement, la différence entre eux réside dans les caractéristiques de performance et le comportement de blocage.
Le plus simple est d'abord
ArrayBlockingQueue
une file d'attente de taille fixe. Donc, si vous définissez la taille sur 10 et essayez d'insérer un 11e élément, l'instruction d'insertion se bloquera jusqu'à ce qu'un autre thread supprime un élément. Le problème d'équité est ce qui se passe si plusieurs threads tentent d'insérer et de supprimer en même temps (en d'autres termes pendant la période où la file d'attente a été bloquée). Un algorithme d'équité garantit que le premier thread qui demande est le premier thread qui obtient. Sinon, un thread donné peut attendre plus longtemps que les autres threads, provoquant un comportement imprévisible (parfois un thread ne prendra que quelques secondes car les autres threads qui ont démarré plus tard ont été traités en premier). Le compromis est qu'il faut des frais généraux pour gérer l'équité, ce qui ralentit le débit.La différence la plus importante entre
LinkedBlockingQueue
etConcurrentLinkedQueue
est que si vous demandez un élément à aLinkedBlockingQueue
et que la file d'attente est vide, votre thread attendra jusqu'à ce qu'il y ait quelque chose. AConcurrentLinkedQueue
reviendra tout de suite avec le comportement d'une file d'attente vide.Lequel dépend si vous avez besoin du blocage. Là où vous avez de nombreux producteurs et un seul consommateur, cela ressemble à ça. D'un autre côté, lorsque vous avez de nombreux consommateurs et un seul producteur, vous n'aurez peut-être pas besoin du comportement de blocage, et serez peut-être heureux de simplement demander aux consommateurs de vérifier si la file d'attente est vide et de continuer si c'est le cas.
la source
ConcurrentLinkedQueue signifie qu'aucun verrou n'est pris (c'est-à-dire qu'aucun appel synchronisé (this) ou Lock.lock ). Il utilisera une opération CAS - Compare and Swap pendant les modifications pour voir si le nœud tête / queue est toujours le même qu'au démarrage. Si tel est le cas, l'opération réussit. Si le nœud tête / queue est différent, il tournera et réessayera.
LinkedBlockingQueue prendra un verrou avant toute modification. Ainsi, vos appels d'offres seraient bloqués jusqu'à ce qu'ils obtiennent le verrou. Vous pouvez utiliser la surcharge d'offre qui prend un TimeUnit pour dire que vous êtes seulement prêt à attendre X temps avant d'abandonner l'ajout (généralement bon pour les files d'attente de type de message où le message est périmé après X nombre de millisecondes).
L'équité signifie que l'implémentation Lock gardera les threads ordonnés. Cela signifie que si le fil A entre puis que le fil B entre, le fil A obtiendra le verrou en premier. Sans équité, ce qui se passe n'est pas vraiment défini. Ce sera probablement le prochain thread qui sera programmé.
Quant à savoir lequel utiliser, cela dépend. J'ai tendance à utiliser ConcurrentLinkedQueue car le temps qu'il faut à mes producteurs pour mettre du travail dans la file d'attente est varié. Je n'ai pas beaucoup de producteurs qui produisent exactement au même moment. Mais le côté consommateur est plus compliqué car le sondage ne passera pas dans un bon état de sommeil. Vous devez gérer cela vous-même.
la source
Le titre de votre question mentionne le blocage des files d'attente. Cependant, ce
ConcurrentLinkedQueue
n'est pas une file d'attente bloquante.Les
BlockingQueue
s sontArrayBlockingQueue
,DelayQueue
,LinkedBlockingDeque
,LinkedBlockingQueue
,PriorityBlockingQueue
etSynchronousQueue
.Certains d' entre eux ne sont manifestement pas en forme à vos besoins (
DelayQueue
,PriorityBlockingQueue
etSynchronousQueue
).LinkedBlockingQueue
etLinkedBlockingDeque
sont identiques, sauf que cette dernière est une file d'attente à deux extrémités (elle implémente l'interface Deque).Puisque ce
ArrayBlockingQueue
n'est utile que si vous souhaitez limiter le nombre d'éléments, je m'en tiens àLinkedBlockingQueue
.la source
ArrayBlockingQueue a une empreinte mémoire inférieure, il peut réutiliser le nœud d'élément, contrairement à LinkedBlockingQueue qui doit créer un objet LinkedBlockingQueue $ Node pour chaque nouvelle insertion.
la source
ArrayBlockingQueue
elle aura une empreinte mémoire bien pire - elle a toujours un grand tableau alloué en mémoire tout le temps, tandis que leLinkedBlockingQueue
aura une empreinte mémoire négligeable lorsqu'il est presque vide.SynchronousQueue
(Tiré d'une autre question )SynchronousQueue
est plus un transfert, alors que leLinkedBlockingQueue
seul permet un seul élément. La différence étant que l'put()
appel à aSynchronousQueue
ne reviendra pas tant qu'il n'y aura pas d'take()
appel correspondant , mais avec unLinkedBlockingQueue
de taille 1, l'put()
appel (vers une file d'attente vide) reviendra immédiatement. C'est essentiellement l'BlockingQueue
implémentation lorsque vous ne voulez pas vraiment de file d'attente (vous ne voulez pas conserver de données en attente).LinkedBlockingQueue
(LinkedList
Mise en œuvre mais pas exactement L'implémentation JDK deLinkedList
It utilise la classe interne statique Node pour maintenir les liens entre les éléments)Constructeur pour LinkedBlockingQueue
Classe de nœud utilisée pour gérer les liens
3. ArrayBlockingQueue (implémentation de tableau)
Constructeur pour ArrayBlockingQueue
IMHO La plus grande différence entre
ArrayBlockingQueue
etLinkedBlockingQueue
est claire du constructeur on a la structure de données sous-jacente Array et autre linkedList .ArrayBlockingQueue
utilise l' algorithme de double condition à un seul verrou etLinkedBlockingQueue
est une variante de l' algorithme "deux verrous file d'attente" et il a 2 verrous 2 conditions (takeLock, putLock)la source
ConcurrentLinkedQueue est sans verrouillage, LinkedBlockingQueue ne l'est pas. Chaque fois que vous appelez LinkedBlockingQueue.put () ou LinkedBlockingQueue.take (), vous devez d'abord acquérir le verrou. En d'autres termes, LinkedBlockingQueue a une mauvaise concurrence. Si vous vous souciez des performances, essayez ConcurrentLinkedQueue + LockSupport.
la source