2014-01-29 11 views
6

Der folgende Code druckt manchmal "valueWrapper.isZero()" auf meinem Windows-PC und einem Mac, , die beide ihre JVM im Servermodus ausführen. Ok dies geschieht, weil der Wert Feld nicht endgültig in der ValueWrapper Klasse, so seine möglichen, daß einiger Thread dem abgestandenen Wert 0.Semantik der lokalen endgültigen Variable im Java Memory Model?

public class ConcurrencyApp { 
    private final Random rand = new Random(System.currentTimeMillis()); 
    private ValueWrapper valueWrapper; 

    private static class ValueWrapper { 
     private int value; 

     public ValueWrapper(int value) { 
      this.value = value; 
     } 

     public boolean isZero() { 
      return value == 0; 
     } 
    } 

    private void go() { 
     while (true) { 
      valueWrapper = new ValueWrapper(randomInt(10, 1024)); 
      Thread thread = new Thread(new Runnable() { 

       @Override 
       public void run() { 
        if (valueWrapper.isZero()) { 
         System.out.println("valueWrapper.isZero()"); 
        } 
       } 
      }); 
      thread.start(); 
     } 
    } 

    private int randomInt(int min, int max) { 
     int randomNum = rand.nextInt((max - min) + 1) + min; 
     return randomNum; 
    } 

    public static void printVMInfos() { 
     String vmName = System.getProperty("java.vm.name"); 
     System.out.println("vm name: " + vmName); 
     int cores = Runtime.getRuntime().availableProcessors(); 
     System.out.println("available cores: " + cores); 
    } 

    public static void main(String[] args) { 
     ConcurrencyApp app = new ConcurrencyApp(); 
     printVMInfos(); 
     app.go(); 
    } 
} 

Aber was ist mit der folgenden Modifikation sieht, hier habe ich eine lokale Finale Variable:

private void go() { 
    while (true) { 
     final ValueWrapper valueWrapper = new ValueWrapper(randomInt(10, 1024)); 
     Thread thread = new Thread(new Runnable() { 

      @Override 
      public void run() { 
       if (valueWrapper.isZero()) { 
        System.out.println("valueWrapper.isZero()"); 
       } 
      } 
     }); 

     thread.start(); 
    } 
} 

Es sieht so aus, dass jetzt kein Thread einen schalen Wert von 0. sieht aber ist dies durch die JMM garantiert? Ein kurzer Blick in die Spezifikation überzeugt mich nicht.

+0

Sind Sie sicher, dass das erste Code-Snippet ohne Endung in der Zeile vor dem Thread ausgeführt wird? –

+0

@DhanaKrishnasamy: Es wird ausgeführt, da ValueWrapper ein Feld der ConcurrencyApp ist – user2867869

Antwort

4

ich mich an einem Punkt Grau nicht, aber ich würde seine akzeptieren, wie seine Antwort vor Ort manchmal auf

Der folgende Code ist druckt "valueWrapper.isZero()" auf meinem Windows-PC und einem Mac, beide laufen ihre JVM im Server-Modus .... Es sieht so aus, dass jetzt kein Thread einen veralteten Wert von 0 sieht. Aber das wird vom JMM garantiert ? Ein kurzer Blick in die Spezifikation hat mich nicht überzeugt.

Der Grund, warum Sie valueWrapper.isZero() gibt true zurück, manchmal, weil valueWrapper sehen verändert, nachdem der start aufgerufen wird und vor run bringt es auf den boolean-Test. Wenn Sie nur eine Instanz erstellt haben, wird diese immer nicht null sein, wie Grau erwähnt.


Der Grund final ValueWrapper valueWrapper = new ValueWrapper(randomInt(10, 1024)); arbeitet die ganze Zeit ist, weil das Feld Thread (und Verfahren) lokal ist und die Semantik für ein lokales Objekt und anonyme innere Klassen ist die ursprüngliche Bezugnahme in die Klasseninstanz zu kopieren.

6

Es sieht so aus, dass jetzt kein Thread einen veralteten Wert von 0 sieht. Aber wird dies vom JMM garantiert? Ein kurzer Blick in die Spezifikation hat mich nicht überzeugt.

Es ist garantiert, aber nicht wegen der final. Wenn Sie einen Thread abzweigen, gibt es eine Vorwarngarantie. Alle Speicheroperationen, die in dem Fork-Thread vor ausgeführt werden, Sie werden einen neuen Thread starten, werden garantiert von dem neuen Thread als vollständig erstellt und veröffentlicht angezeigt. Zitat aus JLS 17.4.4 - Synchronization Order:

Eine Aktion, die einen Thread startet synchronisiert-mit der ersten Aktion im Thread es beginnt.

Dies ist verschiedene von einem final Feld, wenn wir über Objektbau und Verlag sprechen. Wenn ein Feldfinal ist, ist es garantiert, ordnungsgemäß initialisiert zu werden, wenn der Konstruktor beendet und das Objekt in mehreren Threads veröffentlicht wird. In Ihrem Fall ist die final wegen der anonymen Klasse notwendig. Wenn Sie keine anonyme Klasse verwenden und dann die final auf Ihrer ValueWrapper entfernen könnten, wäre Ihr Objekt trotzdem garantiert vollständig aufgebaut.

FYI, siehe hier für final Feldinfo: Java concurrency: is final field (initialized in constructor) thread-safe?