在并发编程中,我们通常会遇到以下三个问题原子性,可见性,有序性
volatile 的特性
- 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。(实现可见性)
- 禁止进行指令重排序。(实现有序性)
- volatile 只能保证对单次读/写的原子性。i++ 这种操作不能保证原子性。
可见性分析
缓存一致性(MESI协议)
内存间交互操作
关于主内存与工作内存之间具体的交互协议 即一个变量如何从主内存拷贝到工作内存、如何从工作内存同步回主内存之类的实现细节 Java内存模型中定义了以下8种操作来完成 虚拟机实现时必须保证下面提及的每一种操作都是原子的、不可再分的(对于double和long类型的变量来说 load、store、read和write操作在某些平台上允许有例外
lock(锁定):作用于主内存的变量 它把一个变量标识为一条线程独占的状态
unlock(解锁):作用于主内存的变量 它把一个处于锁定状态的变量释放出来 释放后的变量才可以被其他线程锁定
read(读取):作用于主内存的变量 它把一个变量的值从主内存传输到线程的工作内存中 以便随后的load动作使用
load(载入):作用于工作内存的变量 它把read操作从主内存中得到的变量值放入工作内存的变量副本中
use(使用):作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎 每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作
assign(赋值):作用于工作内存的变量 它把一个从执行引擎接收到的值赋给工作内存的变量 每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作
store(存储):作用于工作内存的变量 它把工作内存中一个变量的值传送到主内存中 以便随后的write操作使用
write(写入):作用于主内存的变量 它把store操作从工作内存中得到的变量的值放入主内存的变量中
write同时cpu告诉其他cpu,flag的指针失效,其他cpu通过总线嗅探机制设置失效,并读取新值。
中间有状态变更(S->M->E->S)及加解锁操作,来保证数据一致
有序性分析
volatile能禁指令重排序
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
//第一次检测
if (instance == null) {
//同步
synchronized (Singleton.class) {
if (instance == null) {
//多线程环境下可能出现问题的地方
instance = new Singleton();
}
}
}
return instance;
}
}
instance = new Singleton();
会分为以下三步
memory = allocate(); //1.分配对象内存空间
instance(memory); //2.初始化对象
instance = memory; //3.设置instance指向刚分配的内存地址,此时instance != null
多线程下会指令重排序,2和3顺序改变不影响串行语意一致性
memory = allocate(); //1.分配对象内存空间
instance = memory; //3.设置instance指向刚分配的内存地址,此时instance != null,但对象还未初始化!
instance(memory); //2.初始化对象
//禁止指令重排序
private volatile static Singleton instance;