2014-01-29 37 views
6

以下代碼有時會在我的Windows-PC和Mac上運行「valueWrapper.isZero()」,它們都在服務器模式下運行其JVM。 確定這是因爲值字段是不是最終的ValueWrapper類, 所以它可能是一些線程看到陳舊的值爲0Java內存模型中本地最終變量的語義?

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(); 
    } 
} 

但是,我們下面的修改,在這裏我使用了當地最後變量:

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(); 
    } 
} 

現在看起來沒有線程看到的0 過時的值,但就是這個由JMM保證? 規範中的簡短說明並不能說服我。

+0

你確定第一個代碼片段在線程之前沒有最後一行運行嗎? –

+0

@DhanaKrishnasamy:它運行,因爲valueWrapper是ConcurrencyApp的一個字段 – user2867869

回答

4

我解決一點灰都沒有,但我會接受他,因爲他的回答是

下面的代碼點有時在我的Windows PC和Mac上打印「valueWrapper.isZero()」,兩者均以服務器模式運行它們的JVM ....看起來像現在沒有線程看到陳舊值爲0.但是,這是由JMM保證的 ?規範中的簡短說明並不能說服我。

你看到valueWrapper.isZero()返回true有時會因爲valueWrapperstart被調用後,正在發生變化,run之前獲取到布爾測試的原因。如果您只創建了一個實例,則如Grey所述,它將始終不爲零。


原因final ValueWrapper valueWrapper = new ValueWrapper(randomInt(10, 1024));作品所有的時間是因爲該領域是線程(和方法)本地和爲本地對象和匿名內部類的語義是原始參考複製到類實例。

6

看起來像現在沒有線程看到過時的值爲0.但這是由JMM保證?規範中的簡短說明並不能說服我。

這是保證,但不是因爲final。在分叉線程時有一個發生前的保證。任何內存操作在分叉線程之前完成你開始一個新的線程保證被新線程視爲完全構建和發佈。引用自JLS 17.4.4 - Synchronization Order

啓動線程的操作將與其啓動的線程中的第一個操作同步。

這是從final不同當我們談論對象的構造和發佈。如果字段final,那麼當構造函數完成並將對象發佈到多個線程時,可以保證它被正確初始化。在你的情況下,final是必要的,因爲匿名類。如果您沒有使用匿名課程,然後您可以刪除ValueWrapper上的final,則由於上述原因,您的對象仍將保證完全構建。

僅供參考,在這裏看到final字段信息:Java concurrency: is final field (initialized in constructor) thread-safe?