可见性
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++这种计数器的作用,除非能肯定只有一个线程去更新它。