Implémentations de la file d'attente Java, laquelle?
De Javadoc:
- Un ConcurrentLinkedQueue est un bon choix lorsque plusieurs threads de partager l'accès à un ensemble commun. Cette file d'attente ne permet 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 soutient une politique d'équité facultative pour commande de fils de producteur et de consommateur en attente
- LinkedBlockingQueue ont généralement un débit plus élevé que les files d'attente basées sur des réseaux, mais des performances moins prévisibles dans la plupart des applications concurrentes.
j'ai 2 scénarios, l'un nécessite la file d'attente pour soutenir de nombreux producteurs (fils l'utilisant) avec un consommateur et l'autre est l'inverse.
Je ne comprends pas si je devrais utiliser ConcurrentLikedQueue
ou les autres (le tableau ou linkedList implémentations). Pourquoi toutes ces implémentations sont censées être simultanées? Je veux dire, quelqu'un peut m'expliquer quelle est la différence entre ConcurrentLikedQueue
et LinkedBlockingQueue
?
aussi, Quelle est la Politique d'équité facultative dans le ArrayBlockingQueue
s'il vous plaît?
6 réponses
fondamentalement, la différence entre eux sont les caractéristiques de performance et le comportement de blocage.
en prenant la première plus facile, ArrayBlockingQueue
est une file d'attente d'une taille fixe. Donc, si vous réglez la taille à 10, et essayez d'insérer une 11ème élément, l'instruction insert bloqué jusqu'à ce qu'un autre thread supprime un élément. La question de l'é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é bloquer.) Un algorithme d'équité garantit que le premier thread qui demande est le premier thread qui obtient. Dans le cas contraire, un thread donné peut attendre plus longtemps que les autres threads, provoquant un comportement imprévisible (parfois un thread ne prendra que quelques secondes parce que les autres threads qui ont commencé plus tard ont été traités en premier). Le compromis est que cela prend 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 d'un LinkedBlockingQueue
et que la file d'attente est vide, votre thread attendra jusqu'à ce qu'il y ait quelque chose. Un ConcurrentLinkedQueue
reviendra immédiatement avec le comportement d'une file d'attente vide.
sur lequel on dépend si vous avez besoin du blocage. Là où il y a beaucoup de producteurs et un consommateur, on dirait bien. D'autre part, lorsque vous avez de nombreux consommateurs et un seul producteur, vous ne pouvez pas besoin du comportement de blocage, et peut-être heureux de juste avoir les consommateurs de vérifier si la file d'attente est vide, et se déplacer sur si il est.
ConcurrentLinkedQueue signifie qu'aucune écluse n'est prise (c.-à-d. aucune écluse synchronisée(la présente) ou ).verrouiller appels). Il utilisera une opération CAS - Compare et Swap pendant les modifications pour voir si le noeud tête/queue est toujours le même que quand il a commencé. Si oui, l'opération réussit. Si le noeud tête / queue est différent, il tournera autour et essaiera à nouveau.
LinkedBlockingQueue sera verrouillé avant toute modification. Donc vos appels d'offre bloqueraient jusqu'à ce qu'ils obtiennent la serrure. Vous pouvez utiliser la surcharge de l'offre qui prend un temps D'unité pour dire que vous êtes seulement prêt à attendre X quantité de temps avant d'abandonner l'add (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).
équité signifie que l'implémentation de la serrure maintiendra les threads ordonnés. Signification si le fil A entre et puis le fil B entre, le fil A aura la serrure en premier. Sans équité, il est indéfini vraiment ce qui se passe. Il sera très probablement le prochain fil qui est prévu.
comme pour lequel on utilise, cela dépend. J'ai tendance à utiliser ConcurrentLinkedQueue parce que le temps qu'il faut à mes producteurs pour obtenir du travail à mettre sur la file d'attente est diverse. Je n'ai pas beaucoup de producteurs produisant exactement au même moment. Mais le consommateur est plus compliqué parce que sondage ne pas aller dans un bon état de veille. Vous avez à gérer vous-même.
le titre de votre question mentionne le blocage des files d'attente. Toutefois, ConcurrentLinkedQueue
est et non une file d'attente de blocage.
les BlockingQueue
s sont ArrayBlockingQueue
, DelayQueue
, LinkedBlockingDeque
, LinkedBlockingQueue
, PriorityBlockingQueue
, et SynchronousQueue
.
certains d'entre eux ne sont manifestement pas adaptés à votre usage ( DelayQueue
, PriorityBlockingQueue
, et SynchronousQueue
). LinkedBlockingQueue
et LinkedBlockingDeque
sont identiques, sauf ce dernier est une file d'attente à double extrémité (il implémente l'interface Deque).
puisque ArrayBlockingQueue
n'est utile que si vous voulez limiter le nombre d'éléments, Je m'en tiens à LinkedBlockingQueue
.
ArrayBlockingQueue a une empreinte mémoire plus faible, il peut réutiliser l'élément node, pas comme LinkedBlockingQueue qui doivent créer un objet LinkedBlockingQueue$Node pour chaque nouvelle insertion.
-
SynchronousQueue
( tiré d'un autre question )
SynchronousQueue
est plus un transfert, alors que le LinkedBlockingQueue
ne permet qu'un seul élément. La différence étant que l'appel put()
à un appel SynchronousQueue
ne reviendra pas tant qu'il n'y aura pas un appel take()
correspondant, mais avec un appel LinkedBlockingQueue
de taille 1, l'appel put()
(à une file d'attente vide) reviendra immédiatement. C'est essentiellement l' BlockingQueue
implémentation pour quand vous ne voulez pas vraiment une file d'attente (vous ne voulez pas maintenir de données en attente).
-
LinkedBlockingQueue
(LinkedList
implémentation mais pas exactement JDK implémentation deLinkedList
il utilise le noeud de classe interne statique pour maintenir les liens entre les éléments)
Constructeur de 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é pour Maintenir les Liens
static class Node<E> {
E item;
Node<E> next;
Node(E x) { item = x; }
}
3 . ArrayBlockingQueue ( Matrice De Mise En Œuvre )
Constructeur de 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 plus grande différence entre ArrayBlockingQueue
et LinkedBlockingQueue
est clair de constructeur un a tableau de structure de données sous-jacente et autre liste liée .
ArrayBlockingQueue
utilise single-lock double condition de l'algorithme de et LinkedBlockingQueue
est une variante de l'algorithme" two lock queue " et il a 2 serrures 2 conditions (takeLock , putLock)
ConcurrentLinkedQueue est sans verrouillage, LinkedBlockingQueue ne l'est pas. Chaque fois que vous invoquez LinkedBlockingQueue.put () ou LinkedBlockingQueue.take (), vous devez d'abord acquérir la serrure. En d'autres termes, LinkedBlockingQueue a une mauvaise concurrence. Si vous vous souciez de la performance, essayez le support queue + Lock.