Java四种内存屏障详解,LoadLoad、LoadStore、StoreLoad、StoreStore
由于没在官网查到相关资料,以下结论来自互联网第三方博客搜集整理后,准确性无法保证,仅供参考。
屏障作用:
-
可见性:当一条线程修改了一个变量的值,新值会立即被写入主内存,同时其他线程读取该变量时会从主内存中读取最新值,而不是使用线程缓存中的值。
-
有序性:编译器和处理器可能会对指令进行重排以提高性能,但这种重排可能会导致其他线程看到不一致的状态。变量的读写操作前后会插入特定的内存屏障,这些屏障会禁止指令重排,从而保证了操作的顺序性。
屏障类别:
- LoadLoad(读读屏障):先执行屏障前的 读,后执行屏障后的 读。
- LoadStore(读写屏障):先执行屏障前的 读,后执行屏障后的 写。
- StoreLoad(写读屏障):先执行屏障前的 写,后执行屏障后的 读。
- StoreStore(写写屏障):先执行屏障前的 写,后执行屏障后的 写。
插入时机表:
屏障需求 | 第二个操作 | |||
第一个操作 | 普通读 | 普通写 |
volatile读 获取锁 |
volatile写 释放锁 |
普通读 | LoadStore | |||
普通写 | StoreStore | |||
volatile读 获取锁 |
LoadLoad | LoadStore | LoadLoad | LoadStore |
volatile写 释放锁 |
StoreLoad | StoreStore |
代码示例:
class X {
int a, b;
volatile int v, u;
void f() {
int i, j;
i = a; // load a
j = b; // load b
/* 为什么这里volatile读之前不插入屏障?如何保证可见性?
* 因为其他线程的volatile写后的屏障必然会让新值同步过来
* 所以这里无需屏障
*/
i = v; // load v
// LoadLoad - volatile读 和 volatile读 之间插入LoadLoad
j = u; // load u
// LoadStore - volatile读 和 普通写 之间插入LoadStore
a = i; // store a
b = j; // store b
// StoreStore - 普通写 和 volatile写 之间插入StoreStore
v = i; // store v
// StoreStore - volatile写 和 volatile写 之间插入StoreStore
u = j; // store u
// StoreLoad - volatile写 和 volatile读 之间插入StoreLoad
i = u; // load u
// LoadLoad - volatile读 和 普通读 之间插入LoadLoad
// LoadStore - volatile读 和 普通写 之间插入LoadStore
j = b; // load b
a = i; // store a
}
}