web-dev-qa-db-fra.com

Comment utiliser ConcurrentLinkedQueue?

Comment utiliser un ConcurrentLinkedQueue en Java?
Avec ce LinkedQueue, dois-je m'inquiéter de la simultanéité dans la file d'attente? Ou dois-je simplement définir deux méthodes (une pour récupérer des éléments de la liste et une autre pour ajouter des éléments à la liste)?
Remarque: ces deux méthodes doivent évidemment être synchronisées. Droite?


EDIT: Ce que j'essaie de faire est la suivante: j'ai une classe (en Java) avec une méthode pour récupérer des éléments de la file d'attente et une autre classe avec une méthode pour ajouter des éléments à la file d'attente. Les éléments ajoutés et extraits de la liste sont des objets de ma propre classe.

Encore une question: dois-je faire cela avec la méthode remove:

while (queue.size() == 0){ 
  wait(); 
  queue.poll();
}

Je n'ai qu'un consommateur et un producteur.

89

Non, les méthodes n'ont pas besoin d'être synchronisées et vous n'avez pas besoin de définir de méthodes. ils sont déjà dans ConcurrentLinkedQueue, utilisez-les simplement. ConcurrentLinkedQueue effectue toutes les opérations de verrouillage et autres dont vous avez besoin en interne; votre ou vos producteurs ajoutent des données à la file d'attente et vos consommateurs les recherchent.

Tout d'abord, créez votre file d'attente:

Queue<YourObject> queue = new ConcurrentLinkedQueue<YourObject>();

Maintenant, quel que soit l'endroit où vous créez vos objets producteur/consommateur, passez dans la file d'attente pour qu'ils aient un endroit où placer leurs objets (vous pouvez utiliser un setter pour cela, mais je préfère faire ce genre de chose dans un constructeur):

YourProducer producer = new YourProducer(queue);

et:

YourConsumer consumer = new YourConsumer(queue);

et ajoutez-y des éléments dans votre producteur:

queue.offer(myObject);

et prenez des choses dans votre consommateur (si la file est vide, poll () retournera null, alors vérifiez-le):

YourObject myObject = queue.poll();

Pour plus d’informations, voir the Javadoc

MODIFIER:

Si vous devez bloquer en attendant que la file ne soit pas vide, vous voudrez probablement utiliser un LinkedBlockingQueue , et utiliser la méthode take (). Toutefois, LinkedBlockingQueue a une capacité maximale (définie par défaut sur Integer.MAX_VALUE, soit plus de deux milliards) et peut donc être appropriée ou non, selon les circonstances.

Si vous ne disposez que d'un seul thread introduisant des éléments dans la file d'attente et d'un autre élément prenant des éléments hors de la file d'attente, ConcurrentLinkedQueue est probablement excessif. Cela vaut plus pour des centaines, voire des milliers de threads accédant à la file d'attente en même temps. Vos besoins seront probablement satisfaits en utilisant:

Queue<YourObject> queue = Collections.synchronizedList(new LinkedList<YourObject>());

Un avantage de cela est qu'il se verrouille sur l'instance (file d'attente), vous pouvez donc vous synchroniser sur la file d'attente pour garantir l'atomicité des opérations composites (comme expliqué par Jared). Vous NE POUVEZ PAS faire cela avec une ConcurrentLinkedQueue, car toutes les opérations sont effectuées SANS verrouillage sur l'instance (à l'aide de variables Java.util.concurrent.atomic). Vous n'aurez PAS à le faire si vous souhaitez bloquer lorsque la file d'attente est vide, car poll () renverra simplement la valeur null lorsque la file d'attente sera vide et que poll () sera atomique. Vérifiez si poll () renvoie null. Si c'est le cas, attendez (), puis réessayez. Pas besoin de verrouiller.

Finalement:

Honnêtement, je viens d'utiliser une LinkedBlockingQueue. Il est encore excessif pour votre application, mais il y a de fortes chances que cela fonctionne correctement. Si ce n'est pas assez performant (PROFILE!), Vous pouvez toujours essayer autre chose, et cela signifie que vous n'avez pas à traiter avec quelque chose de synchronisé:

BlockingQueue<YourObject> queue = new LinkedBlockingQueue<YourObject>();

queue.put(myObject); // Blocks until queue isn't full.

YourObject myObject = queue.take(); // Blocks until queue isn't empty.

Tout le reste est identique. Mettez probablement ne bloquera pas, car il est peu probable que vous mettiez deux milliards d'objets dans la file d'attente.

150
Adam Jaskiewicz

C'est en grande partie un duplicata d'une autre question .

Voici la section de cette réponse qui est pertinente pour cette question:

Dois-je effectuer ma propre synchronisation si j'utilise Java.util.ConcurrentLinkedQueue?

Les opérations atomiques sur les collections simultanées sont synchronisées pour vous. En d'autres termes, chaque appel individuel dans la file d'attente est garanti thread-safe sans aucune action de votre part. Ce qui ( n'est pas garanti thread-safe est une opération non atomique que vous effectuez sur la collection.

Par exemple, ceci est threadsafe sans aucune action de votre part:

queue.add(obj);

ou

queue.poll(obj);

Toutefois; les appels non atomiques à la file d'attente ne sont pas automatiquement thread-safe. Par exemple, les opérations suivantes sont pas automatiquement threadsafe:

if(!queue.isEmpty()) {
   queue.poll(obj);
}

Ce dernier point n'est pas threadsafe, car il est très possible qu'entre le moment où isEmpty est appelé et l'heure de l'appel, les autres threads auront ajouté ou supprimé des éléments de la file d'attente. La méthode threadsafe pour effectuer ceci est la suivante:

synchronized(queue) {
    if(!queue.isEmpty()) {
       queue.poll(obj);
    }
}

Encore une fois ... les appels atomiques à la file d'attente sont automatiquement thread-safe. Les appels non atomiques ne le sont pas.

33
Jared

C’est probablement ce que vous recherchez en termes de sécurité du fil et de "beauté" lorsque vous essayez de tout consommer dans la file d’attente:

for (YourObject obj = queue.poll(); obj != null; obj = queue.poll()) {
}

Cela garantira que vous quittez lorsque la file est vide et que vous continuez à extraire des objets de celle-ci tant qu'elle n'est pas vide.

6
marq

Utilisez poll pour obtenir le premier élément et add pour ajouter un nouveau dernier élément. C'est ça, pas de synchronisation ou quoi que ce soit d'autre.

6
Hank Gay

ConcurentLinkedQueue est une implémentation très efficace sans attente/verrouillage (voir le javadoc pour référence), donc non seulement vous n'avez pas besoin de synchroniser, mais la file d'attente ne verrouille rien, ce qui la rend pratiquement aussi rapide qu'un non synchronisé (pas de thread sûr) un.

2
siddhadev

Utilisez-le simplement comme une collection non simultanée. Les classes [Collection] simultanées encapsulent les collections régulières afin que vous n'ayez pas à penser à la synchronisation des accès.

Edit: ConcurrentLinkedList n'est pas en fait un wrapper, mais plutôt une meilleure implémentation simultanée. De toute façon, vous n'avez pas à vous soucier de la synchronisation.

1
Ben S