1) Le CopyOnWriteArraySet
est une implémentation assez simple - il a essentiellement une liste d'éléments dans un tableau, et lors de la modification de la liste, il copie le tableau. Les itérations et autres accès qui sont en cours à ce moment continuent avec l'ancien tableau, évitant la nécessité d'une synchronisation entre les lecteurs et les écrivains (bien que l'écriture elle-même doive être synchronisée). Les opérations de réglage normalement rapides (en particulier contains()
) sont assez lentes ici, car les tableaux seront recherchés en temps linéaire.
Utilisez ceci uniquement pour de très petits ensembles qui seront lus (itérés) souvent et rarement modifiés. (Les ensembles d'écouteurs Swings seraient un exemple, mais ce ne sont pas vraiment des ensembles et ne devraient de toute façon être utilisés qu'à partir de l'EDT.)
2) Collections.synchronizedSet
enroulera simplement un bloc synchronisé autour de chaque méthode de l'ensemble d'origine. Vous ne devez pas accéder directement à l'ensemble d'origine. Cela signifie qu'aucune méthode de l'ensemble ne peut être exécutée simultanément (l'une se bloquera jusqu'à ce que l'autre se termine) - c'est thread-safe, mais vous n'aurez pas de concurrence si plusieurs threads utilisent vraiment l'ensemble. Si vous utilisez l'itérateur, vous devez généralement toujours effectuer une synchronisation externe pour éviter les exceptions ConcurrentModificationExceptions lors de la modification de l'ensemble entre les appels d'itérateur. Les performances seront similaires à celles de l'ensemble d'origine (mais avec une surcharge de synchronisation et un blocage si elles sont utilisées simultanément).
Utilisez cette option si vous n'avez qu'une faible concurrence d'accès et que vous voulez vous assurer que toutes les modifications sont immédiatement visibles pour les autres threads.
3) ConcurrentSkipListSet
est l' SortedSet
implémentation simultanée , avec la plupart des opérations de base en O (log n). Il permet l'ajout / suppression et la lecture / itération simultanés, où l'itération peut ou non indiquer les changements depuis la création de l'itérateur. Les opérations en bloc sont simplement de multiples appels uniques, et non de manière atomique - d'autres threads peuvent n'en observer que certains.
Évidemment, vous ne pouvez l'utiliser que si vous avez une commande totale sur vos éléments. Cela ressemble à un candidat idéal pour les situations à forte concurrence, pour des ensembles pas trop grands (à cause du O (log n)).
4) Pour le ConcurrentHashMap
(et l'ensemble qui en dérive): Ici, la plupart des options de base sont (en moyenne, si vous avez un bon et rapide hashCode()
) dans O (1) (mais peuvent dégénérer en O (n)), comme pour HashMap / HashSet. Il y a une concurrence limitée pour l'écriture (la table est partitionnée et l'accès en écriture sera synchronisé sur la partition nécessaire), tandis que l'accès en lecture est entièrement simultané avec lui-même et les threads d'écriture (mais il se peut que les résultats des modifications actuellement en cours ne soient pas encore visibles. écrit). L'itérateur peut ou non voir les changements depuis sa création et les opérations en bloc ne sont pas atomiques. Le redimensionnement est lent (comme pour HashMap / HashSet), essayez donc d'éviter cela en estimant la taille nécessaire à la création (et en utilisant environ 1/3 de plus, car il est redimensionné lorsqu'il est plein aux 3/4).
Utilisez-le lorsque vous avez de grands ensembles, une bonne (et rapide) fonction de hachage et pouvez estimer la taille de l'ensemble et la concurrence nécessaire avant de créer la carte.
5) Y a-t-il d'autres implémentations de cartes concurrentes que l'on pourrait utiliser ici?