41 votes

Garanties volatiles et exécution non conforme

IMPORTANT EDIT je sais à propos de la "passe avant" dans le thread où les deux missions sont passe ma question est: serait-il possible pour un autre thread à la lecture de "b" non-nulle, tandis que "a" est toujours null. Donc, je sais que si vous êtes d'appel doIt() de la même fil que celui où vous l'aviez appelé setBothNonNull(...) alors il ne peut pas jeter une NullPointerException. Mais que faire si on est en appelant doIt()à partir d'un autre thread que celui de l'appel de setBothNonNull(...) ?

Notez que cette question est uniquement à propos de l' volatile mot-clé et l' volatile garanties: il est pas à propos de l' synchronized mot-clé (donc merci de ne pas répondre "vous devez utiliser synchroniser" pour que je n'ai pas de problème à résoudre: je veux simplement comprendre l' volatile garanties (ou de l'absence de garanties) au sujet de l'exécution).

Dire que nous avons un objet contenant deux volatile Chaîne de références qui sont initialisées à null par le constructeur et que nous n'avons qu'un moyen de modifier les deux chaînes de caractères: en appelant setBoth(...) et que nous ne pouvons définir leurs références par la suite, de non-référence null (seul le constructeur est autorisé à mettre à null).

Par exemple (c'est juste un exemple, il n'y a pas encore question):

public class SO {

    private volatile String a;
    private volatile String b;

    public SO() {
        a = null;
        b = null;
    }

    public void setBothNonNull( @NotNull final String one, @NotNull final String two ) {
        a = one;
        b = two;
    }

    public String getA() {
        return a;
    }

    public String getB() {
        return b;
    }

}

Dans setBothNoNull(...), la ligne de l'affectation de la non-nulle du paramètre "a" s'affiche avant la ligne de l'affectation de la non-nulle du paramètre "b".

Alors si je fais cela (encore une fois, il n'y a pas de question, la question est venue prochaine):

doIt() {
    if ( so.getB() != null ) {
        System.out.println( so.getA().length );
    }
}

Ai-je raison de ma compréhension que, en raison de l'exécution que je peux obtenir un NullPointerException?

En d'autres termes: il n'y a aucune garantie que parce que j'ai lu un non-null "b" je vais vous lire une valeur non null "un"?

Parce qu'en raison de out-of-order (multi)du processeur et de la façon dont volatile travaux "b" peut être attribué avant "une"?

volatile garanties qui lit à la suite d'une écriture doit toujours se référer à la dernière valeur écrite, mais ici il y a un out-of-order "de l'émission" droit? (encore une fois, le "problème" est fait exprès pour essayer de comprendre la sémantique de l' volatile mot-clé et la Java du Modèle de Mémoire, non pas pour résoudre un problème).

25voto

Enno Shioji Points 12298

Non, vous n'obtiendrez jamais un NPE. C'est parce qu' volatile a aussi l'effet de mémoire de l'introduction d'un passe-avant la relation. En d'autres termes, elle permet d'éviter la réorganisation de

a = one;
b = two;

Les déclarations ci-dessus, ne sera pas re-commandé, et tous les threads observer valeur one pour a si b a déjà valeur two.

Voici un thread dans lequel David Holmes explique ceci:
http://markmail.org/message/j7omtqqh6ypwshfv#query:+page:1+mi:34dnnukruu23ywzy+état:les résultats

EDIT (réponse pour le suivi): Ce que Holmes dit, le compilateur pourrait en théorie faire une réorganiser si il n'y avait que le thread a. Cependant, il existe d'autres threads, et ils PEUVENT détecter la réorganisation. C'est pourquoi le compilateur n'est PAS autorisé à le faire réorganisation. La java du modèle de mémoire exige le compilateur spécifiquement pour s'assurer qu'aucun fil ne sera jamais à détecter une telle réorganisation.

Mais que faire si on est en appelant doIt() à partir de un autre thread que celui appelant setBothNonNull(...) ?

Non, vous encore ne JAMAIS avoir un NPE. volatile de la sémantique faire imposer inter-thread de la commande. Ce qui signifie que, pour tous les thread, la cession d' one qui se passe avant la cession de l' two.

8voto

Stephen C Points 255558

Ai-je raison de ma compréhension que, en raison de l'exécution que je peux obtenir un NullPointerException? En d'autres termes: il n'y a aucune garantie que parce que j'ai lu un non-null "b" je vais vous lire une valeur non null "un"?

En supposant que les valeurs attribuées à l' a et b ou de non-nulle, je pense que votre compréhension est pas correct. JL dit ceci:

(1) Si x et y sont des actions de la même fil et x est avant y dans le programme de commande, puis hb(x, y).

(2) Si une action x synchronise avec une suite à l'action de y, alors nous avons aussi hb(x, y).

(3) Si hb(x, y) et hb(y, z), puis hb(x, z).

et

(4) l'écriture d'une variable volatile (§8.3.1.4) v synchronise avec toutes les lectures subséquentes de l' v par n'importe quel thread (où ultérieure est défini en fonction de l'ordre de synchronisation).

Le théorème de

Étant donné que le thread n ° 1 a appelés setBoth(...); une fois, et que les arguments étaient des non-nulle, et le thread n ° 2 a observé b non-nulle, alors le thread n ° 2 ne peut alors observer a de la valeur null.

Informel Preuve

  1. Par (1) - hb(écrire(a, null), write(b, non-null)) dans le thread n ° 1
  2. Par (2) et (4) - hb(écrire(b non nul), lire(b non nul))
  3. Par (1) - hb(lu(b, non-nulle), lire(a, XXX)) dans le thread n ° 2,
  4. Par (4) - hb(écrire(a, null), lire(b non nul))
  5. Par (4) - hb(écrire(a, null), lire(a, XXX))

En d'autres mots, l'écriture d'une valeur non nulle à l' a "passe-avant" la lecture de la valeur (XXX) de l' a. La seule façon que XXX peut être null, c'est que si il y avait une autre action de l'écriture de la valeur null a tels que le hb(écrire(a,null), écrire(un,XXX)) et hb(écrire(a,XXX), lire(a,XXX)). Et ce qui est impossible selon la définition du problème, et, par conséquent, XXX ne peut pas être null. CQFD.

Explication - le JLS états que le hb(...) ("passe-avant") relation n'est pas totalement interdire la réorganisation. Toutefois, si hb(xx,yy), puis la réorganisation des actions de xx et yy est uniquement autorisée si le code a le même effet observable que la séquence d'origine.

2voto

Brandon Bodnar Points 6426

J'ai trouvé le post suivant qui explique que volatile possède la même sémantique de classement que synchronisée dans ce cas. Java volatil est puissant

2voto

Bien que Stephen C et les réponses acceptées soient bons et couvrent à peu près tout, il est bon de noter que la variable a n'a pas besoin d'être volatile - et vous n'obtiendrez toujours pas de NPE. En effet, il y aura une relation «passe-avant-avant» entre a = one et b = two , que a soit volatile . Donc, la preuve formelle de Stephen C est toujours valable, il n’est pas nécessaire que a soit volatile.

0voto

kiwicptn Points 334

J'ai lu cette page et trouvé une version non volatile et non synchronisée de votre question:

 class Simple {
    int a = 1, b = 2;
    void to() {
        a = 3;
        b = 4;
    }
    void fro() {
        System.out.println("a= " + a + ", b=" + b);
    }
}
 

fro peut obtenir 1 ou 3 pour la valeur de a et indépendamment, obtenir 2 ou 4 pour la valeur de b .

(Je réalise que cela ne répond pas à votre question mais le complète.)

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