J'ai une carte qui doit être modifiée simultanément par plusieurs threads.
Il semble y avoir trois implémentations de cartes synchronisées différentes dans l'API Java:
Hashtable
Collections.synchronizedMap(Map)
ConcurrentHashMap
D'après ce que je comprends, Hashtable
est une ancienne implémentation (extension de la Dictionary
classe obsolète ), qui a été adaptée plus tard pour s'adapter à l' Map
interface. Bien qu'il soit synchronisé, il semble avoir de graves problèmes d'évolutivité et est déconseillé pour les nouveaux projets.
Mais qu'en est-il des deux autres? Quelles sont les différences entre les cartes renvoyées par Collections.synchronizedMap(Map)
et ConcurrentHashMap
s? Laquelle correspond à quelle situation?
java
dictionary
concurrency
Henning
la source
la source
ConcurrentSkipListMap
est une autreMap
implémentation thread-safe . Conçu pour être hautement simultané sous charge, utilisant l' algorithme Skip List .Réponses:
Pour vos besoins, utilisez
ConcurrentHashMap
. Il permet la modification simultanée de la carte à partir de plusieurs threads sans avoir besoin de les bloquer.Collections.synchronizedMap(map)
crée une carte de blocage qui dégradera les performances, tout en garantissant la cohérence (si elle est utilisée correctement).Utilisez la deuxième option si vous devez garantir la cohérence des données et que chaque thread doit avoir une vue à jour de la carte. Utilisez le premier si les performances sont critiques et que chaque thread insère uniquement des données dans la carte, les lectures ayant lieu moins fréquemment.
la source
Concernant le mécanisme de verrouillage:
Hashtable
verrouille l'objet , tandis que neConcurrentHashMap
verrouille que le seau .la source
Hashtable
ne verrouille pas une partie de la carte. Regardez l'implémentation. Il utilise unesynchronized
clé sans verrou fourni, ce qui signifie qu'il se verrouille entièrementhashtable
à chaque opération.Les «problèmes d'évolutivité» pour
Hashtable
sont présents exactement de la même manièreCollections.synchronizedMap(Map)
- ils utilisent une synchronisation très simple, ce qui signifie qu'un seul thread peut accéder à la carte en même temps.Ce n'est pas vraiment un problème lorsque vous avez des insertions et des recherches simples (à moins que vous ne le fassiez de manière extrêmement intensive), mais devient un gros problème lorsque vous devez parcourir toute la carte, ce qui peut prendre beaucoup de temps pour une grande carte - tout en un thread fait cela, tous les autres doivent attendre s'ils veulent insérer ou rechercher quoi que ce soit.
Il
ConcurrentHashMap
utilise des techniques très sophistiquées pour réduire le besoin de synchronisation et permettre un accès en lecture parallèle par plusieurs threads sans synchronisation et, plus important encore, fournit unIterator
qui ne nécessite aucune synchronisation et permet même de modifier la carte pendant l'interaction (bien qu'il ne garantisse pas si ou les éléments insérés lors de l'itération ne seront pas retournés).la source
ConcurrentHashMap est préférable lorsque vous pouvez l'utiliser - bien qu'il nécessite au moins Java 5.
Il est conçu pour bien évoluer lorsqu'il est utilisé par plusieurs threads. Les performances peuvent être légèrement plus faibles lorsqu'un seul thread accède à la carte à la fois, mais nettement meilleures lorsque plusieurs threads accèdent simultanément à la carte.
J'ai trouvé une entrée de blog qui reproduit un tableau de l'excellent livre Java Concurrency In Practice , que je recommande vivement.
Collections.synchronizedMap n'a de sens que si vous avez besoin d'envelopper une carte avec d'autres caractéristiques, peut-être une sorte de carte ordonnée, comme un TreeMap.
la source
La principale différence entre ces deux est que
ConcurrentHashMap
ne verrouillera qu'une partie des données qui sont mises à jour tandis que d'autres parties de données sont accessibles par d'autres threads. Cependant,Collections.synchronizedMap()
verrouille toutes les données lors de la mise à jour, les autres threads ne peuvent accéder aux données que lorsque le verrou est libéré. S'il existe de nombreuses opérations de mise à jour et un nombre relativement faible d'opérations de lecture, vous devez choisirConcurrentHashMap
.Une autre différence est également que
ConcurrentHashMap
cela ne préservera pas l'ordre des éléments dans la carte transmise. Il est similaire auHashMap
stockage des données. Il n'y a aucune garantie que l'ordre des éléments est préservé. WhileCollections.synchronizedMap()
conservera l'ordre des éléments de la carte transmise. Par exemple, si vous passez unTreeMap
àConcurrentHashMap
, l'ordre des éléments dans leConcurrentHashMap
peut ne pas être le même que l'ordre dans leTreeMap
, maisCollections.synchronizedMap()
préservera l'ordre.De plus,
ConcurrentHashMap
peut garantir qu'il n'y a pas deConcurrentModificationException
levée pendant qu'un thread met à jour la carte et qu'un autre thread traverse l'itérateur obtenu à partir de la carte. Cependant,Collections.synchronizedMap()
n'est pas garanti à ce sujet.Il y a un poste qui démontre les différences de ces deux et aussi le
ConcurrentSkipListMap
.la source
Carte synchronisée:
La carte synchronisée n'est pas non plus très différente de Hashtable et offre des performances similaires dans les programmes Java simultanés. La seule différence entre Hashtable et SynchronizedMap est que SynchronizedMap n'est pas un héritage et vous pouvez encapsuler n'importe quelle carte pour créer sa version synchronisée à l'aide de la méthode Collections.synchronizedMap ().
ConcurrentHashMap:
La classe ConcurrentHashMap fournit une version simultanée du HashMap standard. Il s'agit d'une amélioration de la fonctionnalité synchronizedMap fournie dans la classe Collections.
Contrairement à la table de hachage et à la carte synchronisée, il ne verrouille jamais la carte entière, mais divise la carte en segments et le verrouillage est effectué sur ceux-ci. Il fonctionne mieux si le nombre de threads de lecture est supérieur au nombre de threads d'écriture.
ConcurrentHashMap par défaut est séparé en 16 régions et des verrous sont appliqués. Ce nombre par défaut peut être défini lors de l'initialisation d'une instance ConcurrentHashMap. Lors de la définition de données dans un segment particulier, le verrouillage de ce segment est obtenu. Cela signifie que deux mises à jour peuvent toujours s'exécuter simultanément en toute sécurité si elles affectent chacune des compartiments distincts, minimisant ainsi les conflits de verrouillage et maximisant ainsi les performances.
ConcurrentHashMap ne lève pas d'exception ConcurrentModificationException
ConcurrentHashMap ne lève pas d'exception ConcurrentModificationException si un thread essaie de le modifier pendant qu'un autre l'itère
Différence entre synchornizedMap et ConcurrentHashMap
Collections.synchornizedMap (HashMap) renverra une collection qui est presque équivalente à Hashtable, où chaque opération de modification sur Map est verrouillée sur l'objet Map tandis que dans le cas de ConcurrentHashMap, la sécurité des threads est obtenue en divisant la carte entière en une partition différente en fonction du niveau de concurrence et verrouiller uniquement une partie particulière au lieu de verrouiller toute la carte.
ConcurrentHashMap n'autorise pas les clés nulles ou les valeurs nulles tandis que HashMap synchronisé autorise une clé nulle.
Liens similaires
Link1
Link2
Comparaison des performances
la source
Hashtable
etConcurrentHashMap
n'autorisez pas lesnull
clés ou lesnull
valeurs.Collections.synchronizedMap(Map)
synchronise toutes les opérations (get
,put
,size
, etc.).ConcurrentHashMap
prend en charge la simultanéité complète des récupérations et la simultanéité attendue réglable pour les mises à jour.Comme d'habitude, il y a des compromis simultanés - frais généraux - vitesse impliqués. Vous devez vraiment prendre en compte les exigences détaillées de concurrence de votre application pour prendre une décision, puis tester votre code pour voir s'il est assez bon.
la source
Dans
ConcurrentHashMap
, le verrou est appliqué à un segment au lieu d'une carte entière. Chaque segment gère sa propre table de hachage interne. Le verrou est appliqué uniquement pour les opérations de mise à jour.Collections.synchronizedMap(Map)
synchronise la carte entière.la source
Vous avez raison
HashTable
, vous pouvez l’oublier.Votre article mentionne le fait que, même si HashTable et la classe wrapper synchronisé offrent une sécurité de thread de base en n'autorisant qu'un thread à la fois à accéder à la carte, ce n'est pas une "vraie" sécurité de thread car de nombreuses opérations composées nécessitent toujours une synchronisation supplémentaire, par exemple. exemple:
Cependant, ne pensez pas que ce
ConcurrentHashMap
soit une alternative simple pour unHashMap
avec unsynchronized
bloc typique comme indiqué ci-dessus. Lisez cet article pour mieux comprendre ses subtilités.la source
En voici peu:
1) ConcurrentHashMap verrouille uniquement une partie de la carte mais SynchronizedMap verrouille la totalité du MAp.
2) ConcurrentHashMap a de meilleures performances sur SynchronizedMap et plus évolutif.
3) En cas de lecteur multiple et d'écrivain unique, ConcurrentHashMap est le meilleur choix.
Ce texte provient de la différence entre ConcurrentHashMap et la table de hachage en Java
la source
Nous pouvons garantir la sécurité des threads en utilisant ConcurrentHashMap et synchronizedHashmap et Hashtable. Mais il y a beaucoup de différence si vous regardez leur architecture.
la source
ConcurrentHashMap
SynchronizedHashMap
la source
la source
ConcurrentHashMap est optimisé pour un accès simultané.
Les accès ne verrouillent pas la carte entière mais utilisent une stratégie plus fine, ce qui améliore l'évolutivité. Il existe également des améliorations fonctionnelles spécifiquement pour l'accès simultané, par exemple des itérateurs simultanés.
la source
Il existe une fonctionnalité essentielle à noter
ConcurrentHashMap
autre que la fonctionnalité de concurrence qu'il fournit, qui est l' itérateur de sécurité . J'ai vu des développeurs utiliserConcurrentHashMap
simplement parce qu'ils voulaient modifier le jeu d'entrées - mettre / supprimer tout en itérant dessus.Collections.synchronizedMap(Map)
ne fournit pas fail-safe iterator mais il fournit échec rapide iterator à la place. les itérateurs à échec rapide utilisent un instantané de la taille de la carte qui ne peut pas être modifiée pendant l'itération.la source
la source
En général, si vous souhaitez utiliser le
ConcurrentHashMap
assurez-vous que vous êtes prêt à manquer les «mises à jour»(c'est-à-dire que l'impression du contenu du HashMap ne garantit pas qu'il imprimera la carte à jour) et utilisez des API comme
CyclicBarrier
pour assurer la cohérence à travers votre programme. cycle de la vie.la source
La méthode Collections.synchronizedMap () synchronise toutes les méthodes du HashMap et le réduit efficacement à une structure de données où un thread peut entrer à la fois car il verrouille chaque méthode sur un verrou commun.
Dans ConcurrentHashMap, la synchronisation se fait un peu différemment. Plutôt que de verrouiller chaque méthode sur un verrou commun, ConcurrentHashMap utilise un verrou séparé pour des compartiments distincts, verrouillant ainsi uniquement une partie de la carte. Par défaut, il y a 16 compartiments et également des verrous séparés pour des compartiments séparés. Le niveau de concurrence par défaut est donc 16. Cela signifie théoriquement à tout moment que 16 threads peuvent accéder à ConcurrentHashMap s'ils vont tous dans des compartiments séparés.
la source
ConcurrentHashMap a été présenté comme une alternative à Hashtable dans Java 1.5 dans le cadre du package de concurrence. Avec ConcurrentHashMap, vous avez un meilleur choix non seulement s'il peut être utilisé en toute sécurité dans l'environnement multithread simultané, mais offre également de meilleures performances que Hashtable et synchronizedMap. ConcurrentHashMap fonctionne mieux car il verrouille une partie de la carte. Il permet des opérations de lecture simultanée et en même temps maintient l'intégrité en synchronisant les opérations d'écriture.
Comment ConcurrentHashMap est implémenté
ConcurrentHashMap a été développé comme alternative à Hashtable et prend en charge toutes les fonctionnalités de Hashtable avec des capacités supplémentaires, appelées niveau de concurrence. ConcurrentHashMap permet à plusieurs lecteurs de lire simultanément sans utiliser de blocs. Cela devient possible en séparant la carte en différentes parties et en ne bloquant qu'une partie de la carte dans les mises à jour. Par défaut, le niveau de concurrence est 16, donc la carte est divisée en 16 parties et chaque partie est gérée par un bloc séparé. Cela signifie que 16 threads peuvent fonctionner avec Map simultanément, s'ils fonctionnent avec différentes parties de Map. Il rend ConcurrentHashMap extrêmement productif et ne diminue pas la sécurité des threads.
Si vous êtes intéressé par certaines fonctionnalités importantes de ConcurrentHashMap et quand vous devez utiliser cette réalisation de Map - Je viens de mettre un lien vers un bon article - Comment utiliser ConcurrentHashMap en Java
la source
En plus de ce qui a été suggéré, je voudrais publier le code source lié à
SynchronizedMap
.Pour sécuriser un
Map
thread, nous pouvons utiliserCollections.synchronizedMap
instruction et saisir l'instance de carte comme paramètre.L'implémentation de
synchronizedMap
dansCollections
est comme ci-dessousComme vous pouvez le voir, l'
Map
objet d' entrée est encapsulé par l'SynchronizedMap
objet.Examinons la mise en œuvre de
SynchronizedMap
,Ce
SynchronizedMap
qui peut être résumé comme l'ajout d'un verrou unique à la méthode principale de l'Map
objet d' entrée . Toutes les méthodes gardées par le verrou ne sont pas accessibles par plusieurs threads en même temps. Cela signifie que les opérations normales commeput
etget
peuvent être exécutées par un seul thread en même temps pour toutes les données duMap
objet.Cela fait
Map
thread d'objet sûr maintenant mais les performances peuvent devenir un problème dans certains scénarios.La mise en œuvre
ConcurrentMap
est beaucoup plus compliquée, nous pouvons nous référer à Construire un meilleur HashMap pour plus de détails. En un mot, il est mis en œuvre en tenant compte à la fois du thread safe et des performances.la source