2017-10-31 2 views
1

Ich habe theoretische Frage über Speicher Sichtbarkeit. Hier ist ein Beispielcode:Warum Variable für andere Threads ohne Synchronisation sichtbar?

public class TwoThreadApp { 

    private static class A { 
     int x = 1; 
    } 

    public static void main(String[] arg) throws InterruptedException { 
     A a = new A(); 

     Thread t2 = new Thread(() -> { 
      while (true) { 
       if (a.x == 2) { 
        System.out.println(a.x); 
        return; 
       } 
       // IO operation which makes a.x visible to thread "t2" 
       System.out.println("in loop"); 
      } 
     }); 

     t2.start(); 
     Thread.sleep(100); 
     a.x = 2; 
    } 
} 
  1. Ohne System.out.println("in loop") Programme arbeiten auf unbestimmte Zeit, das Verhalten zu erwarten ist.
  2. Aber mit System.out.println("in loop") ist es immer abgeschlossen, was nicht erwartet wird, weil a.x nicht flüchtig ist und es keine synchronisierten Blöcke gibt.

Mein env: ubuntu 16.04, openjdk 1.8.0_131

Warum es so verhält?

+1

Die Variablen immer zugänglich sind (zum Lesen und Schreiben) becase es ist statisch und in der gleichen Klasse. Die Synchronisierung ist nur eine Möglichkeit, ein Objekt zu sperren, um die Integrität sicherzustellen. – gusto2

+0

Da 'x' zwischengespeichert wird, hat die JVM diese Art von Optimierungen. –

Antwort

0

Ohne System.out.println ("In-Schleife") Programme funktioniert unbegrenzt, was erwartetes Verhalten ist.

Im Gegenteil, das Programm sollte beenden. Das Programm läuft weiter, das ist ein Nebeneffekt der Tatsache, dass x zwischengespeichert wird (als nicht flüchtig). Das Caching ist die Optimierung des Compilers und Sie sollten sich nicht darauf verlassen (abhängig von den JVM-Einstellungen).

Aber mit System.out.println ("in Schleife") ist immer abgeschlossen, was nicht erwartet wird, weil a.x nicht flüchtig ist und es keine synchronisierten Blöcke gibt.

Dies ist IMHO erwartetes Verhalten. Ich kann Ihnen nicht sagen, warum, ich würde annehmen, dass die IO-Operationen beteiligt sind, um den Thread-Cache zu löschen (bitte kommentieren/korrigieren, wenn jemand einen besseren Einblick hat).

Der Zugriff auf Variablen ohne Synchronisierung oder Sperren oder mit mehreren Threads flüchtig sein kann wirklich unvorhersehbar sein.

Sie auch viele optimalizations mit -Djava.compiler=NONE (dann sollte das Programm immer beenden als erwartet)

+0

Die Hauptfrage ist, wie IO-Vorgänge beim Löschen von Thread-Cache beteiligt sind. –

+0

Das war nur meine Annahme. Wenn Sie dekompilieren, gibt der Bytecode für println 'ldc' (statische Daten laden) und' invokevirtual' (Methode aufrufen) an. Das Caching wird wirklich * magisch * durch JIT (Just-in-Time-Compiler) erledigt. Einfach verwenden keine flüchtigen oder synchronisiert Sie machen das Programm unvorhersehbar – gusto2

+0

@SergeyGalkin, es hat nichts mit IO zu tun, aber mit der passiert-vor-Beziehung von der JVM angewendet.Die Verknüpfung mit der Duplikate-Antwort erklärt, wie println funktioniert (siehe "Die Wirkung einer print-Anweisung"), sie enthält einen synchronisierten Block. Gemäß dem Java-Speichermodell (siehe https://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#synchronization): "Bevor wir einen synchronisierten Block eingeben können, erwerben wir der Monitor, der den lokalen Prozessor-Cache ungültig macht, so dass Variablen aus dem Hauptspeicher neu geladen werden. " –

0

IMHO deaktivieren kann, ist dies etwas mit Compiler-Optimierung zu tun, denke ich. In Ihrem ersten Beispiel kompilieren kann entscheiden, den Ist die Bedingung außerhalb while-Schleife wie

if(a.x == 2) while(true) {...} 

Im zweiten Fall zu bewegen, wie Sie println verwenden, die intern verwendet Schlüsselwort Compiler synchronisiert kann den Code wie oben nicht optimieren.

Das ist, was ich denke und ich vielleicht falsch.

Edit: Sie können hier beziehen: Loop doesn't see changed value without a print statement

Verwandte Themen