2016-04-23 125 views
0

我目前正試圖研究併發性,特別是「volatile」關鍵字。Java - 易失性不按預期工作

通過聲明計數器變量揮發性所有寫計數器變量將被立即寫回主內存。此外,計數器變量的所有讀取都將直接從主存儲器讀取。這裏是計數器變量的volatile聲明的外觀

當一個線程寫入volatile變量,那麼不只是volatile變量本身被寫入主存儲器。在寫入volatile變量之前,線程更改的所有其他變量也會刷新到主內存中。當一個線程讀取一個易失變量時,它還會讀取主內存中與易失變量一起刷新到主內存中的所有其他變量。

來源:tutorials.jenkov.com | Java Concurrency - Java Volatile Keyword

這使我得出結論/假設,我做出一個volatile變量的任何變化都會永遠是所有線程可見。所以,我做了一個代碼來測試它。

TestClass

package org.personal.test1; 

class TestClass { 
    public static int w = 0; 
    public static int x = 0; 
    public static int y = 0; 
    public static volatile int z = 0; 
    private static final int ITERATIONS = 100000; 


    public static void sooPlus(int indents) { 
     for (int i = 0; i < TestClass.ITERATIONS; i++) { 
      TestClass.w++; 
      TestClass.x++; 
      TestClass.y++; 
      TestClass.z++; 
     } 
    } 

    public static void sooMinus(int indents) { 
     for (int i = 0; i < TestClass.ITERATIONS; i++) { 
      TestClass.w--; 
      TestClass.x--; 
      TestClass.y--; 
      TestClass.z--; 
     } 
    } 


    public static synchronized String getVariableValues() { 
     StringBuilder stringBuilder = new StringBuilder(); 
     stringBuilder.append("("); 
     stringBuilder.append("w : "+TestClass.w+", "); 
     stringBuilder.append("x : "+TestClass.x+", "); 
     stringBuilder.append("y : "+TestClass.y+", "); 
     stringBuilder.append("z : "+TestClass.z+")"); 
     return stringBuilder.toString(); 
    } 

} 

Main Class

package org.personal.test1; 

/** 
* <ol type="I"> 
*  <li> 
*   <a href="http://tutorials.jenkov.com/java-concurrency/volatile.html">jenkov.com - Java Volatile Keyword</a> 
*  </li> 
* </ol> 
*/ 
public class Main { 

    public static void main(String[] args) { 
     Main.call1(); 
    } 

    private static void call1() { 
     Main.test1(); 
    } 

    private static void test1() { 
     Thread thread1 = new Thread("Thread1") { 
      @Override 
      public void run() { 
       TestClass.sooPlus(1); 
      } 
     }; 

     Thread thread2 = new Thread("Thread2") { 
      @Override 
      public void run() { 
       TestClass.sooMinus(4); 
      } 
     }; 

     thread1.start(); 
     thread2.start(); 

     try { 
      thread1.join(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

     try { 
      thread2.join(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

     System.out.println(TestClass.getVariableValues()); 

    } 
} 

我得到的結果不是我所期待的。

我得到什麼(變化)

(w : -2314, x : -1692, y : -1416, z : -1656) 

我很期待

(w : 0, x : 0, y : 0, z : 0) 

或至少

(w : -2314, x : -1692, y : -1416, z : 0) 

的問題

  1. 我是怎麼假設/推斷錯誤,導致不同的輸出少於預期?
  2. 我的測試方法不正確?如果是,那我該如何解決?
  3. 可選)有什麼好的Java併發性教程,你推薦?

注意

  • 我沒有嘗試讀取類似的問題,但我沒能完全理解提問者試圖以瞭解他的問題做。
+1

volatile關鍵字提供了一種弱的線程安全形式。它保證可見性,但不是原子性或互斥。是否可能會使易變的字段不是線程安全的?是的,如果寫入字段不是原子的。由所有易失性字段組成的類是否可能不是線程安全的?是的,如果寫入字段導致無效狀態轉換(由於缺乏同步)。 – scottb

回答

3

volatile關鍵字提供了弱線程安全的形式。它保證的可見性,但不是原子性或互斥

  • 是否可能使易失性字段不是線程安全的?線程安全。寫入非易失性doublelong值不是原子的,而是寫入易失性doublelong變量。

  • 是否有可能由所有易失性字段組成的類不是線程安全的?是的,如果寫入字段導致無效狀態轉換(由於缺乏同步)。

(可選)有沒有對Java併發任何好的教程,你 建議?

這本書通常被推薦爲這個主題的權威性處理,是由Brian Goetz撰寫的「Java Concurrency in Practice」。它變得有點老了。

+0

-1,對不起。這個答案大部分是正確的(特別是提到*互斥* - 你應該詳細說明),但是volatile寫* *保證是原子的(參見https://docs.oracle.com/javase/specs/ jls/se8/html/jls-17.html#jls-17.7),並且這個回答將(錯誤的)相反的要求放在前面和中心。 – ruakh

+0

我站好了。答案已修改。 – scottb

+0

謝謝; downvote縮回。儘管如此,對於OP的示例代碼來說,真正重要的一點是,像TestClass.w - 這樣的東西包含一個讀操作,然後是一個完全獨立的寫操作,其結果是它不一定實際減少TestClass。 w'。 (它相當於'int tmp = TestClass.w; tmp--; TestClass.w = tmp;',這意味着它可以取代/覆蓋/放棄對TestClass.w的其他更改)。 – ruakh