Les variables statiques finales sont-elles sécurisées en Java?

J’ai lu pas mal de choses mais je n’ai pas trouvé de réponse définitive.

J’ai un cours qui ressemble à ceci:

public class Foo() { private static final HashMap sharedData; private final HashMap myRefOfInnerHashMap; static { // time-consuming initialization of sharedData final HashMap innerMap = new HashMap; innerMap.put... innerMap.put... ...a sharedData.put(someKey, java.util.Collections.unmodifiableMap(innerMap)); } public Foo(Ssortingng key) { this.myRefOfInnerHashMap = sharedData.get(key); } public void doSomethingUseful() { // iterate over copy for (Map.Entry entry : this.myRefOfInnerHashMap.entrySet()) { ... } } } 

Et je me demande s’il est thread-safe pour accéder à sharedData à partir des instances de Foo (comme cela est montré dans le constructeur et dans doSomethingUseful ()). De nombreuses instances de Foo seront créées dans un environnement multithread.

Mon intention est que sharedData soit initialisée dans l’initialiseur statique et non modifiée par la suite (en lecture seule).

Ce que j’ai lu, c’est que les objects immuables sont insortingnsèquement thread-safe. Mais je n’ai vu cela que dans ce qui semble être le contexte des variables d’instance. Les variables statiques immuables sont-elles thread-safe?

L’autre construction que j’ai trouvée était ConcurrentHashMap. Je pourrais créer des données sharedData de type ConcurrentHashMap mais les HashMaps qu’il contient doivent-elles également être de type ConcurrentHashMap? Fondamentalement..

 private static final ConcurrentHashMap sharedData; 

ou

 private static final ConcurrentHashMap sharedData; 

Ou serait-il plus sûr (encore plus coûteux de simplement cloner ())?

 this.myCopyOfData = sharedData.get(key).clone(); 

TIA.

(L’initialiseur statique a été modifié pour donner plus de contexte.)

la référence à sharedData qui est final est thread-safe car il ne peut jamais être modifié. Le contenu de la mappe n’est PAS thread-safe car il doit être enveloppé de préférence avec une implémentation Guava ImmutableMap ou java.util.Collections.unmodifiableMap() ou utiliser l’une des implémentations de java.util.concurrent package java.util.concurrent .

Ce n’est que si vous faites les deux que vous aurez une sécurité complète des threads sur la carte. Toute carte contenue doit être immuable ou l’une des implémentations simultanées.

.clone () est fondamentalement cassé, rest à l’écart

le clonage par défaut est un clone superficiel, il ne renverra que les références aux objects conteneur, pas des copies complètes. Il est bien documenté dans les informations généralement disponibles sur les raisons.

L’initialisation des champs finaux statiques dans un bloc d’initialisation statique est thread-safe. Cependant, rappelez-vous que l’object auquel un sharepoint référence statique final peut ne pas être thread-safe. Si l’object auquel vous faites référence est thread-safe (par exemple, il est immuable), vous êtes en clair.

Chaque HashMap individuel contenu dans votre HashMap externe n’est pas sûr d’être thread-safe à moins que vous n’utilisiez ConcurrentHashMap comme suggéré dans votre question. Si vous n’utilisez pas une implémentation HashMap interne adaptée aux threads, vous risquez d’obtenir des résultats inattendus lorsque deux threads accèdent au même object HashMap interne. Gardez à l’esprit que seules certaines opérations sur ConcurrentHashMap sont synchronisées. Par exemple, l’itération n’est pas thread-safe.

Qu’est-ce que thread-safe? Bien sûr, l’initialisation de HashMap est thread-safe dans la mesure où toutes les Foo partagent la même instance de Map et que la Map est garantie, sauf si une exception se produit dans l’init statique.

Mais modifier le contenu de la carte n’est assurément pas sécuritaire pour les threads. Static final signifie que la carte sharedData ne peut pas être commutée pour une autre carte. Mais le contenu de la carte est une question différente. Si une clé donnée est utilisée plusieurs fois en même temps, vous risquez d’avoir des problèmes de concurrence.

Non, sauf s’ils sont immuables.

La seule chose qu’ils font est

  • Être accessible au niveau de la classe
  • Évitez de changer la référence.

Néanmoins, si votre atsortingbut est modifiable, il n’est pas thread-safe.

Voir aussi: Synchronisons-nous les variables d’instance qui sont finales?

C’est exactement la même chose sauf qu’ils sont au niveau de la classe.

Oui, ceci est sécuritaire aussi. Tous les membres finaux de votre classe statique seront initialisés avant qu’un thread ne soit autorisé à y accéder.

Si le bloc static échoue lors de l’initialisation, une ExceptionInInitializerError est générée dans le thread qui tente la première initialisation. NoClassDefFoundError tentative ultérieure de faire référence à la classe NoClassDefFoundError une NoClassDefFoundError .

En général, le contenu d’un HashMap n’a aucune garantie de visibilité sur les threads. Toutefois, le code d’initialisation de la classe utilise un bloc synchronized pour empêcher plusieurs threads d’initialiser la classe. Cette synchronisation videra l’état de la carte (et les instances HashMap qu’elle contient) afin qu’elles soient correctement visibles par tous les threads, en supposant qu’aucune modification ne soit apscope à la carte ou aux cartes qu’elle contient en dehors de l’initialiseur de classe.

Voir la spécification du langage Java, au § 12.4.2 pour plus d’informations sur l’initialisation de la classe et la configuration requirejse pour la synchronisation.

Il n’y a rien de insortingnsèquement thread-safe sur une variable final static . Le fait de déclarer une variable membre final static garantit uniquement que cette variable est affectée à une seule fois.

La question de la sécurité des threads a moins à voir avec la façon dont vous déclarez les variables mais repose plutôt sur la façon dont vous interagissez avec les variables. Donc, il n’est pas vraiment possible de répondre à votre question sans plus de détails sur votre programme:

  • Plusieurs threads modifient-ils l’état de votre variable sharedData ?
  • Si oui, synchronisez-vous sur toutes les écritures ( et lectures) de sharedData ?

L’utilisation d’un ConcurrentHashMap garantit uniquement que les méthodes individuelles de la Map sont thread-safe, cela ne crée pas d’opération comme celle-ci:

 if (!map.containsKey("foo")) { map.put("foo", bar); } 

Ne demandez-vous pas si l’initialisation statique de sharedData est thread-safe et exécutée une seule fois?

Et oui, c’est le cas.

Bien sûr, beaucoup de personnes ici ont correctement fait remarquer que le contenu de sharedData peut toujours être modifié.

Dans ce cas, seul l’object sharedData est immuable, ce qui signifie que vous travaillerez tout le temps avec le même object. Mais toutes les données qu’il contient peuvent être modifiées (supprimées, ajoutées, etc.) à tout moment, à partir de n’importe quel thread.