Pour autant que je sache, Java.util.Hashtable
synchronise chaque méthode de l'interface Java.util.Map
, tandis que Collections.synchronizedMap(hash_map)
retourne un objet wrapper contenant des méthodes synchronisées déléguant les appels au réel hash_map
(corrigez-moi si je me trompe).
J'ai deux questions:
Quelle différence cela fait-il de synchroniser chaque méthode et d'avoir une classe wrapper? Quels sont les scénarios pour choisir l'un plutôt que l'autre?
Que se passe-t-il lorsque nous faisons Collections.synchronizedMap(hash_table)
? Est-ce que cela équivaut à simplement utiliser un Java.util.Hashtable
Normal?
Voici les réponses que j'ai obtenues d'un peu de recherche (espérons-le correcte):
Les deux offrent le même degré de synchronisation. Si vous deviez envelopper Hashtable
via Collections.synchronized, vous auriez le même degré, mais avec une autre couche redondante, de synchronisation.
La principale différence entre Hashtable
et Collections.synchronizedMap(HashMap)
existe davantage au niveau de l'API. Parce que Hashtable
fait partie du code hérité de Java, vous verrez que l'API Hashtable
est améliorée pour implémenter l'interface Map
, pour faire partie du framework de collections Java. Cela signifie que si vous deviez envelopper Hashtable
à Collections.synchronizedMap()
, l'API du Hashtable
encapsulé deviendrait limitée à l'API Map
. Donc, si l'API de Hashtable
est incluse dans votre définition de comportement, alors elle est évidemment modifiée/limitée.
Une autre différence que je peux trouver lors de l'implémentation des deux classes est la suivante:
• La classe Hashtable
a toutes ses méthodes synchronisées, c'est-à-dire que le verrouillage est fait au niveau de la méthode et donc on peut dire que le mutex est toujours à l'objet Hashtable
( this
) niveau.
• La méthode Collections.synchronizedMap(Map)
renvoie une instance de SynchronizedMap
qui est une classe interne à la classe Collections
. Cette classe a toutes ses méthodes dans un bloc Synchronized
avec un mutex. La différence réside ici dans le mutex. La classe interne SynchronizedMap
a deux constructeurs, un qui ne prend que Map
comme argument et un autre qui prend un Map
et un Object
(mutex) comme un argument. Par défaut, si l'on utilise le premier constructeur de passer uniquement un Map
, this
est utilisé comme mutex. Cependant, le développeur est autorisé à passer un autre objet de mutex comme deuxième argument par lequel le verrou sur les méthodes Map
serait uniquement sur ce Object
et donc moins restrictif que Hashtable
.
• Par conséquent, Hashtable
utilise la synchronisation au niveau de la méthode mais Collections.synchronizedMap(Map)
offre une flexibilité pour verrouiller le développeur sur le mutex fourni avec le bloc Synchronized
.
La première classe de collection associative à apparaître dans la bibliothèque de classes Java était Hashtable, qui faisait partie de JDK 1.0. Hashtable a fourni une capacité de carte associative facile à utiliser et sécurisée pour les threads. était certainement pratique. Cependant, la sécurité des threads avait un prix - toutes les méthodes de Hashtable étaient synchronisées. À cette époque, la synchronisation non intentionnelle avait un coût de performance mesurable. Le successeur de Hashtable, HashMap, qui apparaissait dans le cadre des collections dans JDK 1.2, traitait la sécurité des threads en fournissant une classe de base non synchronisée et un wrapper synchronisé, Collections.synchronizedMap. La séparation de la fonctionnalité de base du thread-safety Collections.synchronizedMap permettait aux utilisateurs qui avaient besoin d'une synchronisation de l'avoir, mais les utilisateurs qui ne l'ont pas fait besoin il n'a pas eu à payer pour cela.
L'approche simple de synchronisation adoptée à la fois par Hashtable et synchronizedMap - la synchronisation de chaque méthode sur Hashtable ou l'objet wrapper de carte synchronisée - présente deux lacunes principales. C'est un obstacle à l'évolutivité, car un seul thread peut accéder à la table de hachage à la fois. Dans le même temps, il est insuffisant pour assurer une véritable sécurité des threads, dans la mesure où de nombreuses opérations composées courantes nécessitent encore une synchronisation supplémentaire. Alors que des opérations simples telles que get () et put () peuvent se terminer en toute sécurité sans synchronisation supplémentaire, il existe plusieurs séquences d'opérations courantes, telles que l'itération ou la mise-si-absent, qui nécessitent toujours une synchronisation externe pour éviter les courses de données.
Le lien suivant est la source et contient plus d'informations: Classes de collections simultanées
La différence n'est pas tout au niveau évident de l'API et il existe de nombreuses subtilités au niveau de l'implémentation. Par exemple, Hashtable
ne possède pas le recalcul avancé de HashMap
des codes de hachage des clés fournies qui réduit les collisions de hachage. D'un autre côté, Hashtable#hashCode()
évite la récursion infinie des tables de hachage autoréférentielles pour permettre à "certaines applets de l'ère 1.1 avec des tables de hachage autoréférentielles de fonctionner".
En général, cependant, il ne faut pas compter sur Hashtable
pour recevoir d'autres améliorations ou raffinements au-delà de l'exactitude de base et de la compatibilité descendante. Il est considéré comme une relique du passé profond Java passé.
Un autre point de différence à noter est que HashTable ne permet pas les clés ou les valeurs nulles alors que HashMap autorise une clé nulle et un nombre illimité de valeurs nulles. Puisque synchronizedMap est wrapper sur HashMap, son comportement par rapport aux valeurs et clés nulles est le même que HashMap.
Au risque d'énoncer l'évidence (ou de se tromper) n'est pas la différence que
Les wrappers de synchronisation ajoutent une synchronisation automatique (thread-safety) à une collection arbitraire
http://docs.Oracle.com/javase/tutorial/collections/implementations/wrapper.html et continue de dire
Une collection créée de cette manière est tout aussi sûre pour les threads qu'une collection normalement synchronisée, telle qu'un vecteur.
Vous aimerez peut-être voir ce fil pour les problèmes concernant les HashMaps et la concurrence - problème de concurrence Hashmap (ou vous les connaissez peut-être déjà très bien). Un bon exemple est:
Les conditions que vous décrivez ne seront pas remplies par HashMap. Étant donné que le processus de mise à jour d'une carte n'est pas atomique, vous pouvez rencontrer la carte dans un état non valide. Plusieurs écritures peuvent le laisser dans un état corrompu. ConcurrentHashMap (1.5 ou version ultérieure) fait ce que vous voulez.
https://stackoverflow.com/a/1003071/201648
Je suppose qu'en termes de "quand dois-je utiliser cela", j'aurais tendance à utiliser la collection synchronisée où la concurrence est requise, sinon vous pourriez créer plus de travail pour vous-même (voir ci-dessous).
En termes de modification du comportement
Si un itérateur explicite est utilisé, la méthode itérateur doit être appelée à partir du bloc synchronisé. Le non-respect de ces conseils peut entraîner un comportement non déterministe
Il y a plus de conséquences de l'utilisation de la synchronisation donnée sur le lien (Oracle) fourni.