web-dev-qa-db-fra.com

est Java HashSet thread-safe pour lecture seule?

Si j'ai une instance d'un HashSet après l'avoir exécuté via Collections.unmodifiableSet (), est-ce thread-safe?

Je pose cette question car la documentation de Set indique que ce n'est pas le cas, mais j'effectue uniquement des opérations de lecture.

27
Asaf Mesika

Du Javadoc:

Notez que cette implémentation n'est pas synchronisée. Si plusieurs threads accèdent simultanément à un ensemble de hachage et qu'au moins l'un des threads modifie l'ensemble, il doit être synchronisé en externe

La lecture ne modifie pas un ensemble, donc ça va.

41
Brian Roach

HashSet sera threadsafe s'il est utilisé en lecture seule. Cela ne signifie pas que any Définissez que vous passez à Collections.unmodifiableSet() sera threadsafe.

Imaginez cette implémentation naïve de contains qui met en cache la dernière valeur vérifiée:

Object lastKey;
boolean lastContains;

public boolean contains(Object key) {
   if ( key == lastKey ) {
      return lastContains;
   } else {
      lastKey = key;
      lastContains = doContains(key);
      return lastContains;
   }
}

De toute évidence, ce ne serait pas threadsafe.

13
Mark Peters

Ce serait sûr pour les threads, mais uniquement parce que Collections.unmodifiableSet() publie en interne la cible Set de manière sûre (via le champ final).

Notez qu'en général, les instructions telles que "les objets en lecture seule sont toujours thread-safe" ne sont pas corrects, car ils ne prennent pas en compte la possibilité de réorganisation des opérations.

Il est (théoriquement) possible qu'en raison de la réorganisation des opérations, une référence à cet objet en lecture seule devienne visible pour les autres threads avant que l'objet ne soit complètement initialisé et rempli de données. Pour éliminer cette possibilité, vous devez publier les références à l'objet de manière sûre, par exemple, en les stockant dans les champs final, comme cela se fait par Collections.unmodifiableSet().

11
axtavt

Chaque structure de données est thread-safe si vous ne la mutez pas.

Parce que vous devez muter un HashSet pour l'initialiser, il est nécessaire de synchroniser une fois entre le thread qui a initialisé l'ensemble et tous les threads de lecture. Vous devez le faire seulement ne fois. Par exemple, lorsque vous passez la référence à l'ensemble non modifiable à un nouveau thread qui ne l'a jamais touché auparavant.

6
jmg

Oui, il est sûr pour l'accès en lecture simultanée. Voici la phrase pertinente de la documentation:

Si plusieurs threads accèdent simultanément à un ensemble de hachage et qu'au moins l'un des threads modifie l'ensemble, il doit être synchronisé en externe.

Il indique que vous devez uniquement synchroniser si at least one thread le modifie.

2
Jonathan

Je ne pense pas que ce soit sûr pour les threads simplement parce que vous exécutez Collections.unmodifiableSet (). Même si le HashSet est complètement initialisé et que vous l'avez marqué comme non modifiable, cela ne signifie pas que ces modifications seront visibles pour les autres threads. Pire encore, en l'absence de synchronisation, un compilateur est autorisé à réorganiser les instructions, ce qui pourrait signifier que non seulement un thread de lecture voit les données manquantes, mais il peut également voir le hachage dans un état étrange. Par conséquent, vous aurez besoin d'une synchronisation. Je crois qu'une solution consiste à créer le hashset comme final et à l'initialiser complètement dans le constructeur. Voici un bon article sur le JMM http://www.cs.umd.edu/~pugh/Java/memoryModel/jsr-133-faq.html . Lisez la section Comment fonctionnent les champs finaux sous le nouveau JMM?

La possibilité de voir la valeur correctement construite pour le champ est agréable, mais si le champ lui-même est une référence, vous souhaitez également que votre code voit les valeurs à jour de l'objet (ou tableau) vers lequel il pointe. Si votre champ est un champ final, cela est également garanti. Ainsi, vous pouvez avoir un pointeur final sur un tableau et ne pas avoir à vous soucier que d'autres threads voient les valeurs correctes pour la référence du tableau, mais des valeurs incorrectes pour le contenu du tableau. Encore une fois, par "correct" ici, nous entendons "à jour à la fin du constructeur de l'objet", pas "la dernière valeur disponible".

2
richs