Comment utiliser un ConcurrentLinkedQueue
en Java?
En utilisant cela LinkedQueue
, dois-je m'inquiéter de la concurrence 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 ceci: 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.
Une autre question: dois-je faire cela dans la méthode remove:
while (queue.size() == 0){
wait();
queue.poll();
}
Je n'ai qu'un seul consommateur et un seul producteur.
la source
Réponses:
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; vos producteurs ajoutent des données dans la file d'attente et vos consommateurs les interrogent.
Tout d'abord, créez votre file d'attente:
Maintenant, où que vous créiez vos objets producteurs / consommateurs, passez dans la file d'attente pour qu'ils aient un endroit où placer leurs objets (vous pouvez utiliser un setter pour cela, à la place, mais je préfère faire ce genre de chose dans un constructeur):
et:
et ajoutez-y des éléments dans votre producteur:
et enlevez des choses dans votre consommateur (si la file d'attente est vide, poll () retournera null, alors vérifiez-le):
Pour plus d'informations, consultez le Javadoc
ÉDITER:
Si vous devez bloquer l'attente pour que la file d'attente ne soit pas vide, vous souhaiterez probablement utiliser LinkedBlockingQueue et utiliser la méthode take (). Cependant, LinkedBlockingQueue a une capacité maximale (par défaut Integer.MAX_VALUE, qui est supérieur à deux milliards) et peut donc ou non être approprié en fonction de votre situation.
Si vous n'avez qu'un seul thread qui met des éléments dans la file d'attente et qu'un autre thread enlève des éléments de la file d'attente, ConcurrentLinkedQueue est probablement excessif. C'est plus lorsque vous pouvez avoir des centaines, voire des milliers de threads accédant à la file d'attente en même temps. Vos besoins seront probablement satisfaits en utilisant:
Un avantage de ceci est qu'il se verrouille sur l'instance (file d'attente), vous pouvez donc synchroniser sur la file d'attente pour assurer l'atomicité des opérations composites (comme expliqué par Jared). Vous NE POUVEZ PAS faire cela avec un ConcurrentLinkedQueue, car toutes les opérations sont effectuées SANS verrouillage sur l'instance (à l'aide des variables java.util.concurrent.atomic). Vous n'aurez PAS besoin de faire cela si vous voulez bloquer pendant que la file d'attente est vide, car poll () renverra simplement null alors que la file d'attente est vide et poll () est atomique. Vérifiez si poll () renvoie null. Si c'est le cas, attendez (), puis réessayez. Pas besoin de verrouiller.
Finalement:
Honnêtement, j'utiliserais juste un LinkedBlockingQueue. C'est encore exagéré 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 à gérer AUCUN truc synchronisé:
Tout le reste est identique. Put ne bloquera probablement pas, car vous ne risquez pas de mettre deux milliards d'objets dans la file d'attente.
la source
Collection.synchronizedList
renvoie unList
qui n'implémente pasQueue
.ConcurrentLinkedQueue
pour Producer Consumer une bonne idée, je fais référence à ce post stackoverflow.com/questions/1426754/…C'est en grande partie le double d'une autre question .
Voici la section de cette réponse qui est pertinente à 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 à la file d'attente est garanti thread-safe sans aucune action de votre part. Ce qui n'est pas garanti thread-safe, ce sont les opérations que vous effectuez sur la collection qui ne sont pas atomiques.
Par exemple, c'est threadsafe sans aucune action de votre part:
ou
Toutefois; les appels non atomiques à la file d'attente ne sont pas automatiquement thread-safe. Par exemple, les opérations suivantes ne sont pas automatiquement threadsafe:
Ce dernier n'est pas threadsafe, car il est très possible qu'entre le moment où isEmpty est appelé et le moment où l'interrogation est appelée, d'autres threads aient ajouté ou supprimé des éléments de la file d'attente. La méthode threadsafe pour effectuer cela est la suivante:
Encore une fois ... les appels atomiques à la file d'attente sont automatiquement thread-safe. Les appels non atomiques ne le sont pas.
la source
Utilisez poll pour obtenir le premier élément et add pour ajouter un nouveau dernier élément. Voilà, pas de synchronisation ou autre chose.
la source
C'est probablement ce que vous recherchez en termes de sécurité des threads et de "jolie" lorsque vous essayez de tout consommer dans la file d'attente:
Cela garantit que vous quittez lorsque la file d'attente est vide et que vous continuez à en retirer les objets tant qu'elle n'est pas vide.
la source
Le ConcurentLinkedQueue est une implémentation sans attente / verrouillage très efficace (voir le javadoc pour référence), donc non seulement vous n'avez pas besoin de synchroniser, mais la file d'attente ne verrouillera rien, étant donc virtuellement aussi rapide qu'un non synchronisé (pas un thread safe) un.
la source
Utilisez-le simplement comme vous le feriez pour une collection non simultanée. Les classes Concurrent [Collection] encapsulent les collections régulières afin que vous n'ayez pas à penser à synchroniser l'accès.
Edit: ConcurrentLinkedList n'est pas en fait juste un wrapper, mais plutôt une meilleure implémentation simultanée. Quoi qu'il en soit, vous n'avez pas à vous soucier de la synchronisation.
la source