volatile是java虚拟机提供的轻量级的同步机制
- 内存可见性 (保证可见性)
- 不保证原子性
- 禁止指令重排 (保证有序性)
可见性
- volatile修饰的共享变量有如下特点
- 线程中读取这个变量时,每次都会读取主内存中最新的值,然后将其复制到工作内存
- 线程中修改了工作内存中变量的副本,修改之后会立即刷新到主内存
有序性
-
计算机在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排
- 源代码 -> 编译器优化的重排 -> 指令并行的重排 -> 内存系统的重排 -> 最终执行的指令
- 单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致。
- 处理器在进行重排序时必须要考虑指令之间的 数据依赖性
- 多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一执行是无法确定的,结果无法预测
-
是通过内存屏障来实现禁止指令重排的
- 内存屏障其实就是一种IVM指令,java内存模型的重排规则会要求java编译器在生成JVM指令时插入特定的内存屏障指令
- 内存屏障之前的所有写操作都要写回主内存
- 内存屏障之后的所有读操作都能获取 内存屏障之前所有写操作的最新结果
- 写屏障
- 告诉处理器在写屏障之前将所有存储在缓存中的数据同步到主内存,也就是说当看到 store屏障指令,就必须把该指令之前的所有写入指令执行完毕才能继续往下执行
- 读屏障
- 处理器在读屏障之后的读操作,都在读屏障之后执行。也就是说在Load屏障指令之后,就能保证后面读取的数据一定是最新的
- 细分屏障类型(调用底层,底层的分类)
- LoadLoad
- StoreStore
- LoadStore
- StoreLoad
- 内存屏障其实就是一种IVM指令,java内存模型的重排规则会要求java编译器在生成JVM指令时插入特定的内存屏障指令
volatile使用场景
- 单一赋值,如boolean、或者int
- 状态标志,判断业务是否结束
- 读多写少的场景,读不用加锁,写操作需要加锁
- 单例模式下的双重检查锁,利用它的禁止指令重排
代码证明
- 可见性
- 工作线程对变量的更改在写回主内存,并将新修改的值同步给其他工作线程。
public class VolatileModel {
volatile int model = 0;
private void updateModel(){
model = 60;
}
public static void main(String[] args) {
VolatileModel volatileModel = new VolatileModel();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " come in.");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
volatileModel.updateModel();
System.out.println(Thread.currentThread().getName() + " data:" + volatileModel.model);
}, "AAA").start();
while(volatileModel.model == 0){
}
System.out.println(Thread.currentThread().getName() + " data:" + volatileModel.model);
}
}
- 不保证原子性
public class Data {
volatile int n = 0;
public void add(){
n++;
}
//result: 结果小于20000
//原因:由于20个线程在进行N++的操作时,都是在自己的工作内存中完成,然后在写会主内存的
// n++是非原子性的,javac -p查看class文件对应的 是3个操作,线程在执行这3个操作会出现插队情况,就会出现原本+1后写会主内存的值 实际上已经被其他线程+1过了
public static void main(String[] args) {
Data d = new Data();
for(int i = 0;i< 20;i++){
new Thread(new Runnable() {
@Override
public void run() {
for(int j = 0;j < 1000;j++){
d.add();
}
}
}).start();
}
while (Thread.activeCount() > 2){
Thread.yield();
}
System.out.println(d.n);
}
}
-
解决volatile原子性问题
- synchronized关键字
- Lock锁
- AtomicInteger类
- cas实现,自旋锁
-
有序性