【面试】Volatile详解

大纲

  • 一、为什么有Volatile?
  • 二、Volatile是什么?
    • 2.1 作用
      • 1、可见性
      • 2、有序性
      • 3、原子性
  • 三、Volatile怎么实现的?
    • 3.1、可见性
    • 3.2、有序性
  • 四、Volatile延申?
  • 五、参考

一、为什么有Volatile?

【面试】Volatile详解_第1张图片

  • 共享变量工作内存缓存(可见性问题)
    每个线程都有一份自己的本地内存,所有线程共用一份主内存。如果一个线程对主内存中的数据进行了修改,而此时另外一个线程不知道是否已经发生了修改,即两个线程有可能会操作同一份但是值不一样的数据。这时候怎么办呢?于是乎,今天的主角登场了,这就是volatile关键字。

  • 指令重排序
    单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致。
    处理器在进行重排序时必须要考虑指令之间的数据依赖性。
    多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程 中使用的变量能否保证一致性是无法确定的,结果无法预测
    ……

二、Volatile是什么?

2.1 作用

1、可见性

当一个线程改完一个共享变量会立刻刷新到主内存,并强制清空该变量的缓存数据,必须从主内存重新读取最新数据,这保证了共享变量在多处理器开发中的可见性。

2、有序性

禁止指令重排序,在一定程度上保证有序性。

volatile关键字禁止指令重排序有两层意思:
1)当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
2)在进行指令优化时,不能将在对volatile变量的读操作或者写操作的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。

3、原子性

volatile不能保证完全原子性,只能保证单次的读/写操作具有原子性。

public class P0Test {
     
    private volatile  int value;  //将value变量声明成volatile类型
    public  void increment(){
      //synchronized
        i++;
        System.out.println(i);
    }
    public static void main(String[] args) {
     
        final P0Test volatileTest1 = new P0Test();
        for(int i = 0; i < 100; i ++){
     
            new Thread(new Runnable() {
     
                @Override
                public void run() {
     
                    volatileTest1.increment();
                }
            }).start();
        }
    }
}
说明:
1、 i++是一个复合操作:
- 读取 i 值;
- 对 i 加 1- 将 i 值写回内存。
volatile无法保证这三个操作有原子性,可通过AtomicIntegerSynchronized来保证+1操作的原子性。

2Thread.sleep()方法是为了增加并发问题的产生几率,无其他作用。

三、Volatile怎么实现的?

3.1、可见性

volatile变量修饰会出现Lock前缀指令,在多核处理器中会引发两件事:

  • 将当前处理器缓存行数据写回到系统内存
  • 然后使其他CPU里缓存了该内存地址的数据无效
在多处理器环境下,为了保证各个处理器缓存一致,每个处理会通过嗅探在总线上传播的数据来检查自己的缓存是否过期,当处理器发现自己缓存行对应的内存地址被修改了,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作时,会强制重新从系统内存把数据读到处理器缓存里。 这确保了其他线程获得的声明了volatile变量都是从主内存中获取最新的。

3.2、有序性

Lock前缀指令实际上相当于一个内存屏障/栅栏,确保指令重排序时不会把其后面指令排到内存屏障之前位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成。

内存屏障 说明
StoreStore 屏障 禁止上面的普通写和下面的 volatile 写重排序
StoreLoad 屏障 防止上面的 volatile 写与下面可能有的 volatile 读/写重排序
LoadLoad 屏障 禁止下面所有的普通读操作和上面的 volatile 读重排序
LoadStore 屏障 禁止下面所有的普通写操作和上面的 volatile 读重排序
内存屏障(memory barries):一组处理器指令,用于实现对内存操作的顺序限制

四、Volatile延申?

为什么 AtomicInteger 可以保证原子性,怎么实现?

面试题:volatile关键字的作用是什么? volatile能保证原子性吗? 之前32位机器上共享的longdouble变量的为什么要用volatile? 现在64位机器上是否也要设置呢? i++为什么不能保证原子性? volatile是如何实现可见性的?  内存屏障。 volatile是如何实现有序性的?  happens-before等 说下volatile的应用场景? synchronized VS Volatile

【面试】Volatile详解_第2张图片
……

五、参考

  • 面试
    1、面试官没想到一个Volatile,我都能跟他扯半小时
    2、关键字: volatile详解

  • 主要内容
    1、Java多线程volatile详解
    2、Java 之 volatile 详解

  • 代码示例
    1、详解java中的并发关键字volatile

  • Geek
    1、33 | 并发中的编译技术(一):如何从语言层面支持线程?
    2、加餐 | 什么是数据的强、弱一致性?

你可能感兴趣的:(面试,Java,面试,java,职场和发展)