一、volatile特性
volatile的两点特性:禁止指令重排序、保证内存可见性。volatile不能保证原子性。
1、禁止指令重排
原理:volatile关键字通过提供内存屏障的方式来防止指令被重排序,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。
volatile禁止指令重排序的一些规则:
- 在每个volatile写操作前插入StoreStore屏障,在写操作后插入StoreLoad屏障;
- 在每个volatile读操作前插入LoadLoad屏障,在读操作后插入LoadStore屏障;
2、保证内存可见性
原理从写指令和读指令分别看:
- 对volatile修饰的变量写时,会使得处理器缓存会写到内存。这保证了共享内存中数据是最新的。
- 一个处理器缓存数据回写会导致其他处理器的缓存无效。处理器使用嗅探技术,不断检测共享内存数据变化,发现数据变化的处理器缓存行会无效,强制执行缓存行填充,这保证了共享内存中的最新数据可以更新到其他处理器。
二、volatile使用场景
由于volatile能保证可见性,但无法保证原子性,因此volatile使用的场景必须满足:
- 对变量的写操作不依赖当前值;
- 该变量不包含在其他变量的不变式中。(如各种判断条件)
volatile使用场景举例:
1、状态标记
如下,线程1正在执行,线程2执行shutdown,利用volatile的可见性,running变量的变更可立即被线程1感知。
// 一个工作线程
class Worker implements Runnable {
private volatile boolean running = true;
public void run() {
// doSomething
}
public void shutdown() {
running = false;
}
}
2、单例模式中的double check
也是利用了volatile的可见性,避免多线程同时取instance和初始化。
class Singleton{
private volatile static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if(instance==null) {
synchronized (Singleton.class) {
if(instance==null)
instance = new Singleton();
}
}
return instance;
}
}
参考文章:
http://www.importnew.com/24082.html