volatile 的内存语义
volatile 修饰的变量具有以下特性:
- 可见性。对一个 volatile 变量的读,总是能看到任意线程对这个 volatile 变量最后的写入。
- 原子性。对任意单个 volatile 变量的读/写具有原子性(除去 double 和 float 类型变量,因为这两种类型变量为 64 位类型)。
一个 volatile 变量的单个读/写操作,与锁对普通变量的读/写,它们之间的执行效果相同。
volatile 的 happens-before 规则:一个 volatile 的写 happens-before 于volatile 的读。
volatile 写–读建立的 happens-before 关系
class ReorderExample {
int a = 0;
boolean flag = false;
public void writer() {
a = 1; // 1
flag = true; // 2
}
public void reader() {
if(flag) { // 3
int i = a * a; // 4
}
}
}
假设线程 A 执行 writer() 方法之后,线程 B 执行 reader() 方法。根据 happnes-before 规则进行分析:
1)根据程序次序规则,1 happens-before 2;3 happens-before 4。
2)根据 volatile 规则,2 happens-before 3。
3)根据 happens-before 的传递性规则,1 happens-before 4。
volatile 的内存语义
volatile 的线程通信理解:
volatile 变量修改后,因为 volatile 的读总能看到 volatile 的写,在对 volatile 修饰变量进行写入操作时,本地内存中的共享变量进行修改后直接刷新到主内存;此时进行 volatile 读时,本地内存直接无效化,需要重新从主内存中获取。即 volatile 的一次写读就是线程之间进行一次通信,保证数据正确。
volatile 内存语义的实现
总结
读 volatile 操作通过将其他线程的本地内存无效化,强制从主内存中获取数据,达到数据实时更新效果。通过该方式,volatile 的写与读操作相当于线程间进行隐式通信。
引申一点,锁的内存语义与 volatile 一致,释放锁后数据必须对获取锁可见。