volatile
1.可见性
原理:X86处理器下:带volatie的变量 翻译成汇编码是:lock addl.. 意思是在寄存器上进行一个空操作 实现可见性的关键是lock前缀!在多核处理器下lock前缀会做两件事 1.把更新的值推送到主存 lock信号保证在处理期间 锁定这块内存的缓存行 并且写回主存 然后使用缓存一致性协议来保证修改的 原子性 缓存一致性协议会阻止同时修改两个以上的缓存行 2.致其他缓存了该变量的缓存行无效 在缓存一致性协议的下 其他处理器会通过cpu的嗅探技术 查看自己的内存的缓存行的数据是否被修改 如果是则致缓存行无效
2.有序性(防止重排序)
·为什么出现指令重排序?-为了提高性能 ·重排的分类: 1.编译器:在不改变单线程程序语义情况下 可以重排序 对于编译器重排JMM的编译器重排规则会禁止某些重排序 2.处理器:数据间不存在依赖性则可以重排序 对于处理器重排 通过加入内存屏障来实现重排序 内存屏障: Store Store Barriers:写-写不能重排序 Store Load Barriers:写-读不能重排序 Load Load Barriers:读-读不能重排序 Load Store Barriers:读-写不能重排序
·happens-before规则 ·线程内的所以操作都happens-before后续操作 ·锁的释放happens-before 随后对这个对象的加锁 ·volatile变量的写 happens-before 在volatile 的读 ·A happens-before B — B happens-before C –> A happens-before C ·as-if-serial原则: 不管怎么重排都不会对有数据依赖的操作重新排序
happens-before和as-if-serial本质是一回事 as-if-serial:保证单线程内程序的结果不变 happens-before:保证正确的同步的多线程程序执行结果不变 as-if-serial:单线程下程序看似是顺序执行的 happens-before:正确同步的多线程下程序看似是顺序执行的
volatile写:加Store Store Barriers 和Store Load Barriers:写-读不能重排序 volatile读:加Load Load Barriers 和Load Store Barriers:写-读不能重排序
应用:单例模式下的DCL为什么用volatile关键字? 原因: 1.创建对象分三步: 开辟内存空间 赋默认值 调用构造 函数赋初始值 栈中的变量的引用指向堆中的地址 2.其中第二步和第三步在多线程的情况下 可能重排序 导致得到一个未初始化完全的对象 3.加了volatile关键字 会在 new instance 前加 Store Store屏障 在 其后加Store Load屏障 保证volatile写 完后才能读
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//DCL单例
public class sigeton{
private static volatile sigeton sgt;
private sigeton(){};
public sigeton getInstance(){
if(sgt==null){
synchronized(this){
if(sgt==null){
sgt = new sigeton();
}
}
}
return sgt;
}
}
3.原子性:
只能保证单一变量的原子性