Depuis les JavaDocs:
J'ai 2 scénarios, l'un requiert la file d'attente pour supporter de nombreux producteurs (les 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?
En outre, quelle est la "politique d'équité optionnelle" dans le ArrayBlockingQueue
?
Fondamentalement, la différence entre eux sont les caractéristiques de performance et le comportement de blocage.
Prenant le plus simple en premier, ArrayBlockingQueue
est une file d’attente de taille fixe. Ainsi, si vous définissez la taille sur 10 et tentez d'insérer un onzième élément, l'instruction d'insertion sera bloquée 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 était bloquée). Un algorithme d'équité garantit que le premier thread demandé est le premier thread obtenu. Sinon, un thread donné peut attendre plus longtemps que les autres threads, ce qui entraîne un comportement imprévisible (un thread prend parfois plusieurs secondes, car les autres threads démarrés 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
et ConcurrentLinkedQueue
est que si vous demandez un élément à un LinkedBlockingQueue
et que la file d'attente est vide, votre thread attendra qu'il y ait quelque chose. Un ConcurrentLinkedQueue
retournera immédiatement avec le comportement d'une file vide.
Lequel dépend si vous avez besoin du blocage. Là où il y a plusieurs producteurs et un seul consommateur, cela semble être le cas. D'autre part, lorsque vous avez plusieurs consommateurs et un seul producteur, vous n'avez peut-être pas besoin du comportement de blocage et vous pouvez simplement demander aux consommateurs de vérifier si la file d'attente est vide et de passer à autre chose si c'est le cas.
ConcurrentLinkedQueue signifie qu'aucun verrou n'est utilisé (c'est-à-dire aucun appel synchronisé (this) ou Lock.lock ). Il utilisera une opération CAS - Compare and Swap lors des modifications pour voir si le nœud head/tail est toujours le même qu’il a commencé. Si c'est le cas, l'opération réussit. Si le nœud head/tail est différent, il tournera et essaiera à nouveau.
LinkedBlockingQueue prendra un verrou avant toute modification. Ainsi, vos appels d'offres resteront bloqués jusqu'à ce qu'ils obtiennent le verrou. Vous pouvez utiliser la surcharge d'offre qui prend un TimeUnit pour indiquer que vous êtes prêt à attendre seulement X fois avant d'abandonner l'ajout (généralement utile pour les files d'attente de type de message où le message est périmé après un nombre X de millisecondes).
L'équité signifie que l'implémentation de verrouillage maintiendra les threads ordonnés. Cela signifie que si le fil A entre et que le fil B entre, le fil A obtiendra le verrou en premier. Sans équité, ce qui se passe n’est vraiment pas défini. Ce sera probablement le prochain fil qui sera planifié.
Quant à celui à utiliser, cela dépend. J'ai tendance à utiliser ConcurrentLinkedQueue parce que le temps qu'il faut à mes producteurs pour trouver du travail à mettre en 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 n’ira pas dans un état de sommeil agréable. Vous devez gérer cela vous-même.
Le titre de votre question mentionne les files d'attente bloquantes. Cependant, ConcurrentLinkedQueue
n'est pas une file d'attente bloquante.
Les BlockingQueue
sont ArrayBlockingQueue
, DelayQueue
, LinkedBlockingDeque
, LinkedBlockingQueue
, PriorityBlockingQueue
et SynchronousQueue
.
Certains d'entre eux ne sont clairement pas adaptés à vos besoins (DelayQueue
, PriorityBlockingQueue
et SynchronousQueue
.). LinkedBlockingQueue
et LinkedBlockingDeque
sont identiques, sauf que cette dernière est une file d'attente à deux extrémités (elle implémente l'interface Deque).
Puisque ArrayBlockingQueue
n’est utile que si vous voulez limiter le nombre d’éléments, je me contenterais de LinkedBlockingQueue
.
ArrayBlockingQueue a une empreinte mémoire inférieure, il peut réutiliser un nœud d'élément, contrairement à LinkedBlockingQueue qui doit créer un objet LinkedBlockingQueue $ Node pour chaque nouvelle insertion.
SynchronousQueue
(extrait d'un autre question )SynchronousQueue
est plus un transfert, alors que LinkedBlockingQueue
n'autorise qu'un seul élément. La différence étant que l'appel put()
à un SynchronousQueue
ne sera pas renvoyé tant qu'il n'y aura pas d'appel correspondant take()
, mais avec un LinkedBlockingQueue
de taille 1, Un appel put()
(vers une file vide) reviendra immédiatement. C'est essentiellement l'implémentation BlockingQueue
lorsque vous ne voulez pas vraiment de file d'attente (vous ne voulez pas conserver les données en attente).
LinkedBlockingQueue
(LinkedList
implémentation mais pas exactement JDK Implémentation de LinkedList
utilise une classe interne statique Node pour gérer les liens entre les éléments)Constructeur pour LinkedBlockingQueue
public LinkedBlockingQueue(int capacity)
{
if (capacity < = 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node< E >(null); // Maintains a underlying linkedlist. ( Use when size is not known )
}
Classe de nœud utilisée pour gérer les liens
static class Node<E> {
E item;
Node<E> next;
Node(E x) { item = x; }
}
3 ArrayBlockingQueue (implémentation de tableau)
Constructeur pour ArrayBlockingQueue
public ArrayBlockingQueue(int capacity, boolean fair)
{
if (capacity < = 0)
throw new IllegalArgumentException();
this.items = new Object[capacity]; // Maintains a underlying array
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
IMHO La plus grande différence entre ArrayBlockingQueue
et LinkedBlockingQueue
est claire à partir du constructeur on a la structure de données sous-jacente Array et une autre linkedList .
ArrayBlockingQueue
utilise algorithme à simple verrouillage à deux conditions et LinkedBlockingQueue
est une variante de l'algorithme "deux files d'attente" et comporte 2 conditions de verrouillage (2) (takeLock, putLock)
ConcurrentLinkedQueue est sans verrouillage, pas LinkedBlockingQueue. Chaque fois que vous appelez LinkedBlockingQueue.put () ou LinkedBlockingQueue.take (), vous devez d'abord acquérir le verrou. En d'autres mots, LinkedBlockingQueue a une faible concurrence. Si vous vous souciez des performances, essayez ConcurrentLinkedQueue + LockSupport.