【温故而知新】谈一谈volatile禁止指令重排

  • Post author:
  • Post category:其他


首先,说一下,翻了一下Java编程思想,在第680页,对原子性、可变性的描述。有兴趣,可以翻一番 thinking in Java

做了以下总结:

1、volatile概念

volatile是java虚拟机提供的轻量级同步机制

volatile三个特性:

  • 保证可见性


  • 不保证原子性

  • 禁止指令重排

2、volatile禁止指令重排


(1)指令重排有序性:


计算机在执行程序时,为了提高性能,编译器和处理器常常会做指令重排,一般分为以下三种:

单线程环境里面确保程序最终执行结果和代码顺序执行结果一致。

处理器在进行指令重排序时必须考虑指令之间的数据依赖性

多线程环境中线程交替执行,由于编译器指令重排的存在,两个线程使用的变量能否保证一致性是无法确认的,结果无法预测。

指令重排案例分析one:

public void mySort() {

int x = 11; // 语句1

int y = 12; // 语句2

x = x + 5; // 语句3

y = x * x; // 语句4

}

// 指令重排之后,代码执行顺序有可能是以下几种可能?

// 语句1 -> 语句2 -> 语句3 -> 语句4

// 语句1 -> 语句3 -> 语句2 -> 语句4

// 语句2 -> 语句1 -> 语句3 -> 语句4

// 问题:请问语句4可以重排后变为第1条吗?

// 不能,因为处理器在指令重排时必须考虑指令之间数据依赖性。

(2)禁止指令重排底层原理:


volatile实现禁止指令重排优化,从而避免多线程环境下程序出现乱序执行的现象。

先了解下概念,内存屏障(Memory Barrier)又称内存栅栏,是一个CPU指令,它的作用有两个:

保证特定操作执行的顺序性;

保证某些变量的内存可见性(利用该特性实现volatile内存可见性)

volatile实现禁止指令重排优化底层原理:

由于编译器和处理器都能执行指令重排优化。如果在指令间插入一条Memory Barrier则会告诉编译器和CPU,不管什么指令都不能和这条Memory Barrier指令重排,也就是说通过插入内存屏障,就能禁止在内存屏障前后的指令执行重排优化。内存屏障另外一个作用就是强制刷出各种CPU的缓存数据,因此任何CPU上的线程都能读取到这些数据的最新版本。

左边:写操作场景:先LoadStore指令,后LoadLoad指令。

右边:读操作场景:先LoadLoad指令,后LoadStore指令。

3、volatile使用场景

1)饿汉式单例模式的写法:线程安全

2)懒汉式单例模式的写法:非线程安全

3)双检锁单例模式的写法:线程安全

单例模式(DCL-Double Check Lock双端检锁机制)

如果此时你也把volatile禁止指令重排底层原理也解释清楚了,面试官可能会接着问你,你知道volatile使用场景吗?

单例模式(DCL-Double Check Lock双端检锁机制)就是它的使用场景



版权声明:本文为qq_33792843原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。