2011-10-20 12 views
6

我想了解一下不安全遞減的結果/ Java中的線程遞增,所以我的程序:線程不安全遞減/遞增 - 爲什麼主要是正面的?

主要類:

public class Start { 

    public static void main(String[] args) { 

     int count = 10000000, pos = 0, neg = 0, zero = 0; 

     for (int x=0; x<10000; x++) { 

      Magic.counter = 0; 

      Thread dec = new Thread(new Magic(false, count)); 
      Thread inc = new Thread(new Magic(true, count)); 

      dec.start(); 
      inc.start(); 

      try { 
       inc.join(); 
       dec.join(); 
      } catch (InterruptedException e) { 
       System.out.println("Error"); 
      } 

      if (Magic.counter == 0) 
       zero++; 
      else if (Magic.counter > 0) 
       pos++; 
      else 
       neg++; 
     } 

     System.out.println(Integer.toString(neg) + "\t\t\t" + Integer.toString(pos) + "\t\t\t" + Integer.toString(zero)); 
    } 
} 

主題類:

public class Magic implements Runnable { 

    public static int counter = 0; 

    private boolean inc; 
    private int countTo; 

    public Magic(boolean inc, int countTo) { 
     this.inc = inc; 
     this.countTo = countTo; 
    } 

    @Override 
    public void run() { 

     for (int i=0;i<this.countTo;i++) { 

      if (this.inc) 
       Magic.counter++; 
      else 
       Magic.counter--; 
     } 

    } 
} 

我已經運行編程幾次,並總是獲得更積極的結果,然後消極。我也嘗試改變哪些線程開始的順序,但這並沒有改變。一些結果:

Number of results < 0 | Number of results > 0 | Number of results = 0 

1103    8893    4 
3159    6838    3 
2639    7359    2 
3240    6755    5 
3264    6728    8 
2883    7112    5 
2973    7021    6 
3123    6873    4 
2882    7113    5 
3098    6896    6 
+0

你可能想使Magic.counter變量波動,否則操作可以重新排序和/或它的價值可能每個線程的寄存器進行緩存。 – prunge

回答

6

我敢打賭,你會看到完全相反的行爲有以下變化(即,反枝不改變任何東西):

if (this.inc) 
    Magic.counter--; // note change, and lie about `this.inc` 
else 
    Magic.counter++; 

如果真,什麼可能這是否表示關於線程交互的這個表示?

現在,爲了好玩,讓Magic.counter易變 - [結果如何變化?

除去volatileif/elselock怎麼辦? (A lock確保完整的內存圍欄並建立關鍵區域,應始終產生完美結果。)

快樂編碼。


需要考慮的事情:

  1. 代碼只看小於或大於零,而不是總的漂移/變化:+1或-1是所有需要乍舌。 (擴展收集的數據可能會更有用。)
  2. 由於需要跳轉,所以執行「else」分支需要的時間稍長一些;通常這是一個非問題,但超過1000萬個週期......一個或兩個並不多。
  3. 缺乏揮發性/記憶柵欄的葉子太多 lee-way在Magic.counter變量的可見性。 (我相信符合JVM實際上可能會產生更糟糕的結果...)
  4. ++--運算符本質上是非原子的。
  5. 線程交錯通常是「非確定性」的。如果在多個內核中執行,則更少。
+0

更改Magic.counter的順序 -/++「幫助」一點點,但它不完全相反:)它像5,5k負面,4,5k正面 – Nazin

+0

@Nazin有趣的,謝謝你回報:)其中一個問題是++和 - 是非原子的,線程以這樣一種方式交織,即讀/寫是「拆分」(線程之間的操作原子性和值可見性)。 'volatile'應該有助於值可見性,但是不足以確保關鍵區域(因此是原子)'++/- '操作。 – 2011-10-20 22:10:51

+0

@Nazin也許有人對[特定] JVM實現有更復雜的細節可能知道爲什麼'++'和'--'在這方面似乎不完全對稱。我可以在這個級別提供的最好的是「非確定性」;-) – 2011-10-20 22:15:43

0

一般來說,這是由於Java內存模型的工作原理。您正在兩個不同的線程中訪問共享變量而不同步。變量聲明不穩定,也不會運行atomar操作。 缺少協調和atomar或volatile變量將導致內部優化由JVM執行的線程代碼。此外,未跨越內存屏障的非易失性變量(即​​)將導致每個線程緩存值 - 因此兩個緩存中的線程本地副本衝突。由於在Java中缺乏順序一致性模型,複雜的運行時優化以及所使用的JVM和底層系統(單核或多核,超線程)的特性,所以不可能確定性地預測結果 - 僅僅因爲它違反了Java語言模型的幾個多線程約定。在同一臺機器上運行完全相同的代碼可能仍然會導致類似的結果,但由於線程調度,其他操作系統進程的CPU使用等影響,它們不可能完全相同。

這裏有一些資源有關JMM:http://www.cs.umd.edu/~pugh/java/memoryModel/