可見性
Visibility這玩意兒很微妙,很容易違反直覺。要在多線程之間讀寫內存保證可見性,需要使用同步:always use the proper synchronization whenever data is shared across threads.
Stale Data
public class MutableInteger { private int value; public int get() { return value; } public void set(int value) { this.value = value; } }
對於這個程序,即使對setter方法加上同步也是不夠的,其它讀的線程依然可能讀到修改之前或之後的數據。
對於64位的數字型變量,double或long,則不止是stale data的問題,因爲JVM會將64位的讀寫操作分成兩個32位的操作。所以有可能讀到完全錯誤的數據。
鎖和可見性
可以用鎖來保證可見性。Locking is not just about mutual exclusion; it is also about memory visibility. To ensure that all threads see the most up-to-date values of shared mutable variables, the reading and writing threads must synchronize on a common lock.
Volatile變量
也可以用volatile來保證可見性。要使用volatile變量,必須滿足下面三個條件
- Writes to the variable do not depend on its current value, or you can ensure that only a single thread ever updates the value;
- The variable does not participate in invariants with other state variables; and
- Locking is not required for any other reason while the variable is being accessed.
鎖既能保證原子性又能保證可見性,而volatile只能保證可見性。volatile最常用在表示完成、中斷或者狀態這樣的標誌位上。volatile不能保證像count++這種計數器的作用,除非能肯定只有一個線程去更新它。