3 votes

Objets partiellement construits dans le modèle de mémoire Java

Je suis tombé sur le code suivant dans un article quelque part sur Internet :

public class MyInt {

    private int x;

    public MyInt(int y) {
        this.x = y;
    }

    public int getValue() {
        return this.x;
    }
}

L'article précise que

Les constructeurs ne font pas l'objet d'un traitement spécial par le compilateur (JIT, CPU, etc.). Il est donc permis de réordonner les instructions du constructeur et les instructions qui viennent après le constructeur.

Aussi, cet article de la JSR-133 sur le modèle de mémoire Java indique que

Un thread qui ne peut voir une référence à un objet qu'après que cet objet ait été complètement initialisé est assuré de voir les valeurs correctement initialisées pour les champs finaux de cet objet.

L'article susmentionné MyInt semble immuable (sauf que la classe n'est pas marquée final ) et thread-safe, mais les articles indiquent qu'il ne l'est pas. Ils indiquent qu'il n'est pas garanti que x a toujours la valeur correcte à la lecture.

Mais je pensais que

seul le thread qui crée un objet doit y avoir accès pendant sa construction.

et le Tutoriels Java semblent soutenir cela.

Ma question est la suivante : cela signifie-t-il qu'avec la JMM actuelle, un thread peut avoir accès à un objet partiellement construit en raison du réordonnancement des instructions ? Et si oui, comment ? Et cela signifie-t-il que l'affirmation des Tutoriels Java est tout simplement fausse ?

5voto

David Wallace Points 23911

Cet article dit que si vous avez un code comme

foo = new MyInt(7);

dans une classe qui possède un champ

MyInt foo;

puis les instructions qui reviennent à

(reference to new object).x = 7;
foo = (reference to new object);

pourraient être échangés dans le cadre d'une sorte d'optimisation. Cela ne changera jamais le comportement du thread qui exécute ce code, mais il est possible qu'un autre thread soit capable de lire le code de l'utilisateur. foo après la ligne

foo = (reference to new object);

mais avant la ligne

(reference to new object).x = 7;

dans ce cas, il verrait foo.x comme 0 pas 7 . C'est à dire que cet autre fil pourrait exécuter

int bar = someObject.getFoo().getValue();

et se retrouver avec bar égal à 0 .

Je n'ai jamais vu une telle chose se produire dans la nature, mais l'auteur semble savoir de quoi il parle.

1voto

biziclop Points 21446

Le réordonnancement des instructions ne peut à lui seul conduire un autre thread à voir un objet partiellement construit. Par définition, la JVM n'est autorisée à réorganiser les choses que si elles n'affectent pas un thread. correctement synchronisé le comportement du programme.

C'est la publication non sécurisée de la référence de l'objet qui permet aux mauvaises choses de se produire. Voici une tentative particulièrement pauvre de singleton par exemple :

public class BadSingleton {
   public static BadSingleton theInstance;

   private int foo;

   public BadSingleton() {
      this.foo = 42;
      if (theInstance == null) {
         theInstance = this;
      }
   }
}

Ici, vous publiez accidentellement la référence à l'objet en cours de construction dans un fichier static champ. Ce ne serait pas nécessairement un problème jusqu'à ce que la JVM décide de réorganiser les choses et place this.foo = 42 après l'affectation à theInstance . Ainsi, ces deux éléments conspirent pour briser vos invariants et permettre à un autre thread de voir un fichier BadSingleton.theInstance avec son foo non initialisé.

Une autre source fréquente de publication accidentelle est appeler des méthodes surmontables à partir du constructeur . Cela ne conduit pas toujours à une publication accidentelle, mais le potentiel est là, et il faut donc l'éviter.

seul le thread qui crée un objet doit y avoir accès pendant sa construction.

Et cela veut-il dire que la déclaration des Tutoriels Java est simplement fausse ?

Oui et non. Cela dépend de la façon dont nous interprétons le mot should . Il n'y a aucune garantie que, dans tous les cas possibles, un autre thread ne verra pas un objet partiellement construit. Mais c'est vrai dans le sens où vous debe écrire un code qui ne permet pas que cela se produise.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X