Est-il sûr d'obtenir des valeurs d'un java.util.HashMap à partir de plusieurs threads (sans modification)?

138

Il y a un cas où une carte sera construite, et une fois qu'elle est initialisée, elle ne sera plus jamais modifiée. Il sera cependant accessible (via get (key) uniquement) à partir de plusieurs threads. Est-il sûr d'utiliser un java.util.HashMapde cette manière?

(Actuellement, j'utilise avec plaisir un java.util.concurrent.ConcurrentHashMap, et je n'ai aucun besoin mesuré pour améliorer les performances, mais je suis simplement curieux de savoir si un simple HashMapsuffirait. Par conséquent, cette question n'est pas "Lequel dois-je utiliser?", Ni une question de performances. La question est plutôt "Serait-ce sûr?")

Dave L.
la source
4
De nombreuses réponses ici sont correctes concernant l'exclusion mutuelle des threads en cours d'exécution, mais incorrectes concernant les mises à jour de la mémoire. J'ai voté en conséquence, mais il y a encore de nombreuses réponses incorrectes avec des votes positifs.
Heath Borders
@Heath Borders, si l'instance a était une HashMap non modifiable initialisée statiquement, elle devrait être sûre pour une lecture simultanée (car d'autres threads n'ont pas pu manquer les mises à jour car il n'y avait pas de mises à jour), n'est-ce pas?
kaqqao
S'il est initialisé statiquement et jamais modifié en dehors du bloc statique, cela peut être correct car toute l'initialisation statique est synchronisée par le ClassLoader. Cela vaut une question distincte en soi. Je le synchroniserais toujours explicitement et le profil pour vérifier qu'il causait de réels problèmes de performances.
Heath Borders
@HeathBorders - qu'entendez-vous par «mises à jour de mémoire»? La JVM est un modèle formel qui définit des choses comme la visibilité, l'atomicité, les relations qui se produisent avant , mais n'utilise pas de termes comme «mises à jour de mémoire». Vous devez clarifier, de préférence en utilisant la terminologie du JLS.
BeeOnRope
2
@Dave - Je suppose que vous ne cherchez toujours pas de réponse après 8 ans, mais pour mémoire, la principale confusion dans presque toutes les réponses est qu'elles se concentrent sur les actions que vous effectuez sur l'objet de la carte . Vous avez déjà expliqué que vous ne modifiez jamais l'objet, ce n'est donc pas pertinent. Le seul "piège" potentiel est alors la façon dont vous publiez la référence au Map, ce que vous n'avez pas expliqué. Si vous ne le faites pas en toute sécurité, ce n'est pas sûr. Si vous le faites en toute sécurité, c'est le cas . Détails dans ma réponse.
BeeOnRope

Réponses:

55

Votre idiome est en sécurité si et seulement si la référence à HashMapest publiée en toute sécurité . Plutôt que tout ce qui concerne les éléments internes d' HashMapelle-même, la publication sécurisée traite de la façon dont le thread de construction rend la référence à la carte visible aux autres threads.

Fondamentalement, la seule course possible ici se situe entre la construction du HashMapet les fils de lecture qui peuvent y accéder avant qu'il ne soit entièrement construit. La majeure partie de la discussion porte sur ce qui arrive à l'état de l'objet de la carte, mais cela n'a pas d'importance puisque vous ne le modifiez jamais - donc la seule partie intéressante est la façon dont la HashMapréférence est publiée.

Par exemple, imaginez que vous publiez la carte comme ceci:

class SomeClass {
   public static HashMap<Object, Object> MAP;

   public synchronized static setMap(HashMap<Object, Object> m) {
     MAP = m;
   }
}

... et à un moment donné setMap()est appelé avec une carte, et d'autres threads utilisent SomeClass.MAPpour accéder à la carte, et vérifiez la valeur null comme ceci:

HashMap<Object,Object> map = SomeClass.MAP;
if (map != null) {
  .. use the map
} else {
  .. some default behavior
}

Ce n'est pas sûr même si cela semble probablement le cas. Le problème est qu'il n'y a pas se produit, avant la relation entre l'ensemble des SomeObject.MAPet la lecture ultérieure sur un autre fil, de sorte que le fil de lecture est libre de voir une carte partiellement construite. Cela peut faire n'importe quoi et même en pratique, cela fait des choses comme mettre le fil de lecture dans une boucle infinie .

Pour publier la carte en toute sécurité, vous devez établir une relation d' avance entre l' écriture de la référence à la HashMap(c.-à-d. La publication ) et les lecteurs suivants de cette référence (c.-à-d. La consommation). Idéalement, il n'y a que quelques - uns faciles à retenir les moyens d' y parvenir que [1] :

  1. Échangez la référence via un champ correctement verrouillé ( JLS 17.4.5 )
  2. Utilisez l'initialiseur statique pour effectuer l'initialisation des magasins ( JLS 12.4 )
  3. Echangez la référence via un champ volatil ( JLS 17.4.5 ), ou en conséquence de cette règle, via les classes AtomicX
  4. Initialisez la valeur dans un champ final ( JLS 17.5 ).

Les plus intéressants pour votre scénario sont (2), (3) et (4). En particulier, (3) s'applique directement au code que j'ai ci-dessus: si vous transformez la déclaration de MAPen:

public static volatile HashMap<Object, Object> MAP;

alors tout est casher: les lecteurs qui voient une non nulle valeur ont nécessairement se passe-avant relation avec le magasin pour MAPet donc voir tous les magasins associés à l'initialisation de la carte.

Les autres méthodes changent la sémantique de votre méthode, puisque les deux (2) (en utilisant l'initaliseur statique) et (4) (en utilisant final ) impliquent que vous ne pouvez pas définir MAPdynamiquement au moment de l'exécution. Si vous n'avez pas besoin de le faire, déclarez simplement en MAPtant que static final HashMap<>et vous êtes assuré d'une publication sécurisée.

En pratique, les règles sont simples pour un accès sécurisé aux "objets jamais modifiés":

Si vous publiez un objet qui n'est pas intrinsèquement immuable (comme dans tous les champs déclarés final) et:

  • Vous pouvez déjà créer l'objet qui sera affecté au moment de la déclaration a : il suffit d'utiliser un finalchamp (y compris static finalpour les membres statiques).
  • Vous souhaitez affecter l'objet ultérieurement, après que la référence soit déjà visible: utilisez un champ volatile b .

C'est tout!

En pratique, c'est très efficace. L'utilisation d'un static finalchamp, par exemple, permet à la JVM de supposer que la valeur est inchangée pendant la durée de vie du programme et de l'optimiser fortement. L'utilisation d'un finalchamp membre permet à la plupart des architectures de lire le champ d'une manière équivalente à une lecture de champ normale et n'empêche pas d'autres optimisations c .

Enfin, l'utilisation de volatilea un certain impact: aucune barrière matérielle n'est nécessaire sur de nombreuses architectures (telles que x86, en particulier celles qui ne permettent pas aux lectures de passer des lectures), mais une optimisation et une réorganisation peuvent ne pas se produire au moment de la compilation - mais cela l'effet est généralement faible. En échange, vous obtenez en fait plus que ce que vous avez demandé - non seulement vous pouvez en publier un en toute sécurité HashMap, mais vous pouvez stocker autant de HashMaps non modifiés que vous le souhaitez sur la même référence et être assuré que tous les lecteurs verront une carte publiée en toute sécurité. .

Pour plus de détails sanglants, reportez-vous à Shipilev ou à cette FAQ de Manson et Goetz .


[1] Citation directe de shipilev .


a Cela semble compliqué, mais ce que je veux dire, c'est que vous pouvez attribuer la référence au moment de la construction - soit au point de déclaration, soit dans le constructeur (champs membres) ou l'initialiseur statique (champs statiques).

b En option, vous pouvez utiliser une synchronizedméthode pour obtenir / définir, ou un AtomicReferenceou quelque chose, mais nous parlons du travail minimum que vous pouvez faire.

c Certaines architectures avec des modèles de mémoire très faibles (je regarde vous , Alpha) peuvent nécessiter un certain type de barrière de lecture avant une finallecture - mais ceux - ci sont très rares aujourd'hui.

BeeOnRope
la source
never modify HashMapne signifie pas que le state of the map objectthread est sûr, je pense. Dieu connaît l'implémentation de la bibliothèque, si le document officiel ne dit pas qu'elle est thread-safe.
Jiang YD
@JiangYD - vous avez raison, il y a là une zone grise dans certains cas: lorsque nous disons "modifier", ce que nous voulons vraiment dire, c'est toute action qui effectue en interne des écritures qui pourraient courir avec des lectures ou des écritures sur d'autres threads. Ces écritures peuvent être des détails d'implémentation internes, donc même une opération qui semble "en lecture seule" get()pourrait en fait effectuer des écritures, par exemple la mise à jour de certaines statistiques (ou dans le cas de la LinkedHashMapmise à jour de l'ordre d'accès par ordre d'accès). Donc, une classe bien écrite devrait fournir une documentation qui indique clairement si ...
BeeOnRope
... apparemment, les opérations en "lecture seule" sont réellement en lecture seule en interne dans le sens de la sécurité des threads. Dans la bibliothèque standard C ++ par exemple, il existe une règle générale selon laquelle les fonctions membres marquées constsont vraiment en lecture seule dans ce sens (en interne, elles peuvent toujours effectuer des écritures, mais celles-ci devront être sécurisées pour les threads). Il n'y a pas de constmot-clé en Java et je ne connais aucune garantie globale documentée, mais en général, les classes de bibliothèque standard se comportent comme prévu et les exceptions sont documentées (voir l' LinkedHashMapexemple où les opérations RO comme getsont explicitement mentionnées comme dangereuses).
BeeOnRope
@JiangYD - enfin, pour revenir à votre question d'origine, car HashMapnous avons en fait dans la documentation le comportement de sécurité des threads pour cette classe: si plusieurs threads accèdent à une carte de hachage simultanément, et qu'au moins l'un des threads modifie la carte structurellement, il doit être synchronisé en externe. (Une modification structurelle est toute opération qui ajoute ou supprime un ou plusieurs mappages; le simple fait de changer la valeur associée à une clé qu'une instance contient déjà n'est pas une modification structurelle.)
BeeOnRope
Donc, pour les HashMapméthodes que nous prévoyons d'être en lecture seule, elles sont en lecture seule, car elles ne modifient pas structurellement le fichier HashMap. Bien sûr, cette garantie peut ne pas être valable pour d'autres Mapimplémentations arbitraires , mais la question concerne HashMapspécifiquement.
BeeOnRope
70

Jeremy Manson, le dieu quand il s'agit du modèle de mémoire Java, a un blog en trois parties sur ce sujet - parce que vous vous posez essentiellement la question "Est-il sûr d'accéder à un HashMap immuable" - la réponse est oui. Mais vous devez répondre au prédicat à cette question qui est - "Mon HashMap est-il immuable". La réponse pourrait vous surprendre: Java a un ensemble de règles relativement compliqué pour déterminer l'immuabilité.

Pour plus d'informations sur le sujet, lisez les articles de blog de Jeremy:

Partie 1 sur l'immuabilité en Java: http://jeremymanson.blogspot.com/2008/04/immutability-in-java.html

Partie 2 sur l'immuabilité en Java: http://jeremymanson.blogspot.com/2008/07/immutability-in-java-part-2.html

Partie 3 sur l'immuabilité en Java: http://jeremymanson.blogspot.com/2008/07/immutability-in-java-part-3.html

Taylor Gautier
la source
3
C'est un bon point, mais je compte sur l'initialisation statique, au cours de laquelle aucune référence ne s'échappe, donc cela devrait être sûr.
Dave L.
5
Je ne vois pas en quoi c'est une réponse très appréciée (ou même une réponse). D'une part, il ne répond même pas à la question et il ne mentionne pas le principe clé qui décidera s'il est sûr ou non: une publication sûre . La «réponse» se résume à «c'est délicat» et voici trois liens (complexes) que vous pouvez lire.
BeeOnRope
Il répond à la question à la toute fin de la première phrase. En termes de réponse, il soulève le fait que l'immutabilité (mentionnée dans le premier paragraphe de la question) n'est pas simple, ainsi que des ressources précieuses qui expliquent ce sujet plus en détail. Les points ne mesurent pas s'il s'agit d'une réponse, ils mesurent si la réponse a été "utile" aux autres. La réponse acceptée signifie que c'était la réponse que le PO recherchait, que votre réponse a reçue.
Jesse
@Jesse il ne répond pas à la question à la fin de la première phrase, il répond à la question "est-il sûr d'accéder à un objet immuable", qui peut s'appliquer ou non à la question du PO comme il le souligne dans la phrase suivante. Essentiellement, il s'agit d'une réponse de type «allez-y vous-même», presque uniquement liée aux liens, ce qui n'est pas une bonne réponse pour SO. En ce qui concerne les votes positifs, je pense que c'est plus une fonction d'avoir 10,5 ans et un sujet fréquemment recherché. Il n'a reçu que très peu de votes positifs au cours des dernières années, alors peut-être que les gens viennent :).
BeeOnRope
35

Les lectures sont sûres du point de vue de la synchronisation mais pas du point de vue de la mémoire. C'est quelque chose qui est largement mal compris parmi les développeurs Java, y compris ici sur Stackoverflow. (Observez la note de cette réponse pour preuve.)

Si vous avez d'autres threads en cours d'exécution, ils peuvent ne pas voir une copie mise à jour du HashMap s'il n'y a pas d'écriture de mémoire dans le thread actuel. Les écritures en mémoire se produisent via l'utilisation des mots clés synchronisés ou volatils, ou via l'utilisation de certaines constructions de concurrence Java.

Voir l'article de Brian Goetz sur le nouveau modèle de mémoire Java pour plus de détails.

Frontières de la santé
la source
Désolé pour la double soumission Heath, je n'ai remarqué la vôtre qu'après avoir soumis la mienne. :)
Alexander le
2
Je suis juste content qu'il y ait d'autres personnes ici qui comprennent réellement les effets de mémoire.
Heath Borders
1
En effet, même si aucun thread ne verra l'objet avant qu'il ne soit initialisé correctement, je ne pense donc pas que ce soit un problème dans ce cas.
Dave L.
1
Cela dépend entièrement de la manière dont l'objet est initialisé.
Bill Michell
1
La question dit qu'une fois que le HashMap a été initialisé, il n'a pas l'intention de le mettre à jour davantage. À partir de là, il veut simplement l'utiliser comme une structure de données en lecture seule. Je pense qu'il serait prudent de le faire, à condition que les données stockées dans sa carte soient immuables.
Binita Bharati
9

Après un peu plus de recherche, j'ai trouvé ceci dans la doc java (c'est moi qui souligne):

Notez que cette implémentation n'est pas synchronisée. Si plusieurs threads accèdent simultanément à une mappe de hachage et qu'au moins un des threads modifie la mappe structurellement, elle doit être synchronisée en externe. (Une modification structurelle est toute opération qui ajoute ou supprime un ou plusieurs mappages; le simple fait de changer la valeur associée à une clé qu'une instance contient déjà n'est pas une modification structurelle.)

Cela semble impliquer que ce sera sûr, en supposant que l'inverse de l'énoncé est vrai.

Dave L.
la source
1
Bien que ce soit un excellent conseil, comme l'indiquent d'autres réponses, il existe une réponse plus nuancée dans le cas d'une instance de carte immuable et publiée en toute sécurité. Mais vous ne devriez le faire que si vous savez ce que vous faites.
Alex Miller le
1
J'espère qu'avec des questions comme celles-ci, nous sommes plus nombreux à savoir ce que nous faisons.
Dave L.
Ce n'est pas vraiment correct. Comme l'état des autres réponses, il doit y avoir un événement avant entre la dernière modification et toutes les lectures "thread-safe" suivantes. Normalement, cela signifie que vous devez publier l'objet en toute sécurité après sa création et ses modifications. Voir la première réponse correcte marquée.
markspace
9

Une note est que dans certaines circonstances, un get () à partir d'un HashMap non synchronisé peut provoquer une boucle infinie. Cela peut se produire si un put () simultané provoque une refonte de la carte.

http://lightbody.net/blog/2005/07/hashmapget_can_cause_an_infini.html

Alex Miller
la source
1
En fait, j'ai vu cela accrocher la JVM sans consommer de processeur (ce qui est peut-être pire)
Peter Lawrey
2
Je pense que ce code a été réécrit de telle sorte qu'il n'est plus possible d'obtenir la boucle infinie. Mais vous ne devriez toujours pas essayer d'obtenir et de mettre à partir d'un HashMap non synchronisé pour d'autres raisons.
Alex Miller
@AlexMiller même en dehors des autres raisons (je suppose que vous faites référence à la publication sécurisée), je ne pense pas qu'un changement d'implémentation devrait être une raison pour desserrer les restrictions d'accès, à moins que cela ne soit explicitement autorisé par la documentation. En l' occurrence , le Javadoc HashMap pour Java 8 contient toujours cet avertissement:Note that this implementation is not synchronized. If multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally.
shmosel
8

Il y a cependant une torsion importante. Il est sûr d'accéder à la carte, mais en général, il n'est pas garanti que tous les threads verront exactement le même état (et donc les mêmes valeurs) du HashMap. Cela peut se produire sur les systèmes multiprocesseurs où les modifications apportées au HashMap par un thread (par exemple, celui qui l'a rempli) peuvent se trouver dans le cache de ce processeur et ne seront pas vues par les threads exécutés sur d'autres processeurs, jusqu'à ce qu'une opération de clôture de mémoire soit effectuée en assurant la cohérence du cache. La spécification du langage Java est explicite sur celle-ci: la solution est d'acquérir un verrou (synchronisé (...)) qui émet une opération de clôture mémoire. Donc, si vous êtes sûr qu'après avoir rempli le HashMap, chacun des threads acquiert N'IMPORTE QUEL verrou, alors vous pouvez à partir de ce moment accéder au HashMap à partir de n'importe quel thread jusqu'à ce que le HashMap soit à nouveau modifié.

Alexandre
la source
Je ne suis pas sûr que le thread qui y accède obtiendra un verrou, mais je suis sûr qu'ils n'obtiendront une référence à l'objet qu'après son initialisation, donc je ne pense pas qu'ils puissent avoir une copie périmée.
Dave L.
@Alex: La référence au HashMap peut être volatile pour créer les mêmes garanties de visibilité mémoire. @Dave: Il est possible de voir des références à de nouveaux objets avant que le travail de son cteur ne devienne visible pour votre thread.
Chris Vest
@Christian Dans le cas général, certainement. Je disais que dans ce code, ce n'est pas le cas.
Dave L.
L'acquisition d'un verrou RANDOM ne garantit pas l'effacement de tout le cache du processeur de thread. Cela dépend de l'implémentation de la JVM, et ce n'est probablement pas le cas.
Pierre
Je suis d'accord avec Pierre, je ne pense pas qu'acquérir une serrure suffira. Vous devez synchroniser sur le même verrou pour que les modifications deviennent visibles.
damluar
5

Selon http://www.ibm.com/developerworks/java/library/j-jtp03304/ # Sécurité d'initialisation, vous pouvez faire de votre HashMap un champ final et une fois que le constructeur aura terminé, il sera publié en toute sécurité.

... Sous le nouveau modèle de mémoire, il y a quelque chose de similaire à une relation d'avance entre l'écriture d'un champ final dans un constructeur et la charge initiale d'une référence partagée à cet objet dans un autre thread. ...

Bodrin
la source
Cette réponse est de mauvaise qualité, c'est la même que la réponse de @taylor gauthier mais avec moins de détails.
Snicolas
1
Ummmm ... pas pour être un âne, mais tu l'as à l'envers. Taylor a dit "non, allez regarder ce billet de blog, la réponse pourrait vous surprendre", alors que cette réponse ajoute en fait quelque chose de nouveau que je ne savais pas ... A propos d'une relation qui arrive avant l'écriture d'un champ final dans un constructeur. Cette réponse est excellente et je suis content de l'avoir lue.
Ajax
Hein? C'est la seule réponse correcte que j'ai trouvée après avoir parcouru les réponses les mieux notées. La clé est publiée en toute sécurité et c'est la seule réponse qui la mentionne même.
BeeOnRope
3

Ainsi, le scénario que vous avez décrit est que vous devez mettre un tas de données dans une carte, puis lorsque vous avez terminé de la remplir, vous la traitez comme immuable. Une approche "sûre" (ce qui signifie que vous faites en sorte qu'elle soit réellement traitée comme immuable) consiste à remplacer la référence par Collections.unmodifiableMap(originalMap)lorsque vous êtes prêt à la rendre immuable.

Pour un exemple de la façon dont les cartes peuvent échouer si elles sont utilisées simultanément, et la solution de contournement suggérée que j'ai mentionnée, consultez cette entrée de parade de bogues: bug_id = 6423457

Volonté
la source
2
Ceci est "sûr" en ce sens qu'il applique l'immuabilité, mais ne résout pas le problème de sécurité des threads. Si l'accès à la carte est sécurisé avec l'encapsuleur UnmodifiableMap, il est sécurisé sans lui, et vice versa.
Dave L.
2

Cette question est abordée dans le livre "Java Concurrency in Practice" de Brian Goetz (extrait 16.8, page 350):

@ThreadSafe
public class SafeStates {
    private final Map<String, String> states;

    public SafeStates() {
        states = new HashMap<String, String>();
        states.put("alaska", "AK");
        states.put("alabama", "AL");
        ...
        states.put("wyoming", "WY");
    }

    public String getAbbreviation(String s) {
        return states.get(s);
    }
}

Étant donné que statesest déclaré as finalet que son initialisation est effectuée dans le constructeur de classe du propriétaire, tout thread qui lira plus tard cette carte est assuré de la voir à la fin du constructeur, à condition qu'aucun autre thread n'essaie de modifier le contenu de la carte.

escudero380
la source
1

Soyez averti que même dans un code à thread unique, le remplacement d'un ConcurrentHashMap par un HashMap peut ne pas être sûr. ConcurrentHashMap interdit null comme clé ou valeur. HashMap ne les interdit pas (ne demandez pas).

Donc, dans la situation peu probable où votre code existant pourrait ajouter une valeur null à la collection lors de l'installation (probablement dans un cas d'échec quelconque), le remplacement de la collection comme décrit modifiera le comportement fonctionnel.

Cela dit, à condition que vous ne fassiez rien d'autre, les lectures simultanées à partir d'un HashMap sont sûres.

[Edit: par "lectures simultanées", je veux dire qu'il n'y a pas également de modifications simultanées.

D'autres réponses expliquent comment s'en assurer. Une façon est de rendre la carte immuable, mais ce n'est pas nécessaire. Par exemple, le modèle de mémoire JSR133 définit explicitement le démarrage d'un thread comme une action synchronisée, ce qui signifie que les modifications apportées dans le thread A avant de démarrer le thread B sont visibles dans le thread B.

Mon intention n'est pas de contredire ces réponses plus détaillées sur le modèle de mémoire Java. Cette réponse vise à souligner que même en dehors des problèmes de concurrence, il existe au moins une différence d'API entre ConcurrentHashMap et HashMap, ce qui pourrait même saboter un programme à un seul thread qui remplaçait l'un par l'autre.]

Steve Jessop
la source
Merci pour l'avertissement, mais il n'y a aucune tentative d'utiliser des clés ou des valeurs nulles.
Dave L.
Je pensais qu'il n'y en aurait pas. les nulls dans les collections sont un coin fou de Java.
Steve Jessop le
Je ne suis pas d'accord avec cette réponse. «Les lectures simultanées à partir d'un HashMap sont sûres» en soi est incorrect. Il n'indique pas si les lectures se produisent sur une carte qui est mutable ou immuable. Pour être correct, il devrait lire "Les lectures simultanées d'un HashMap immuable sont sûres"
Taylor Gautier
2
Pas selon les articles auxquels vous avez vous-même lié: la condition est que la carte ne doit pas être modifiée (et les modifications précédentes doivent être visibles pour tous les fils de lecture), pas qu'elle soit immuable (qui est un terme technique en Java et est un condition suffisante mais non nécessaire pour la sécurité).
Steve Jessop
A noter également ... l'initialisation d'une classe se synchronise implicitement sur le même verrou (oui, vous pouvez bloquer les initialiseurs de champs statiques), donc si votre initialisation se produit de manière statique, il serait impossible pour quiconque de la voir avant la fin de l'initialisation, car ils devraient être bloqués dans la méthode ClassLoader.loadClass sur le même verrou acquis ... Et si vous vous interrogez sur des chargeurs de classes différents ayant des copies différentes du même champ, vous auriez raison ... mais ce serait orthogonal au notion de conditions de race; les champs statiques d'un chargeur de classe partagent une clôture de mémoire.
Ajax
0

http://www.docjar.com/html/api/java/util/HashMap.java.html

voici la source de HashMap. Comme vous pouvez le constater, il n'y a absolument aucun code de verrouillage / mutex.

Cela signifie que même s'il est correct de lire à partir d'un HashMap dans une situation multithread, j'utiliserais certainement un ConcurrentHashMap s'il y avait plusieurs écritures.

Ce qui est intéressant, c'est que .NET HashTable et Dictionary <K, V> ont un code de synchronisation intégré.

FlySwat
la source
2
Je pense qu'il existe des classes où la simple lecture simultanée peut vous causer des problèmes, en raison de l'utilisation interne de variables d'instance temporaires, par exemple. Il faut donc probablement examiner attentivement la source, plus qu'une analyse rapide du code de verrouillage / mutex.
Dave L.
0

Si l'initialisation et chaque put est synchronisé, vous êtes sauvegardé.

Le code suivant est enregistré car le classloader se chargera de la synchronisation:

public static final HashMap<String, String> map = new HashMap<>();
static {
  map.put("A","A");

}

Le code suivant est sauvegardé car l'écriture de volatile se chargera de la synchronisation.

class Foo {
  volatile HashMap<String, String> map;
  public void init() {
    final HashMap<String, String> tmp = new HashMap<>();
    tmp.put("A","A");
    // writing to volatile has to be after the modification of the map
    this.map = tmp;
  }
}

Cela fonctionnera également si la variable membre est finale car final est également volatile. Et si la méthode est un constructeur.

TomWolk
la source