理解volatile

1.volatile简介java

     

/**
 * @author Lin
 * @Date 2018/2/22.
 */
public class TestVolatile {

    private static boolean isOver = false;

    public static void main(String[] args) {
        new Thread( () -> {while (!isOver){
            System.out.println("111");
        }} ).start();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        isOver = true;
        System.out.println("End");
    }
}

先看一段代码,启动一个线程,因为isOver = false,因此该线程中run方法的while是死循环。企图在main方法中更改isOver的值去终止线程,可是实时上该程序的线程并无被终止,始终陷入死循环中,线程并无终止退出。缓存

首先咱们对volatile的基本认知是“被volatile修饰的变量对每一个线程是可见的,即一个线程修改了某个变量的值,这新值对其余线程来讲是当即可见的,从而避免数据脏读”。spa

2.深刻理解volatile关键字线程

  1. volatile保证可见性

    当变量被volatile修饰时会具有两种语意:
      1)保证了不一样线程对这个变量进行操做时的可见性,即一个线程修改了某个变量的值,这新值对其余线程来讲是当即可见的。
            解释下上面代码中线程不能被终止,熟悉java内存相关知识的都知道每一个线程在运行时都有本身的工做内存,那么上面线程运行是会将isOver变量拷贝一份到本身的工做内存中,当main线程去修改isOver的          值时,并不会影响到自定义线程工做内存中的isOver的值。
            可是使用volatile修饰就不同了:
           1)使用volatile关键字会强制将修改的值当即写入主存
           2)使用volatile以后当main线程修改isOver后会致使自定义线程工做内存中的isOver无效(反映到硬件层的话,就是CPU的L1或者L2缓存中对应的缓存行无效),因此自定义线程再次读取变量isOver的值时会            去主存读取。
      2)禁止进行指令重排序

  2. volatile不确保原子性(此处要注意,很少记录

3.volatile的实现原理code

    可见性:blog

         在生成汇编代码时会在volatile修饰的共享变量进行写操做的时候会多出Lock前缀的指令。咱们想这个Lock指令确定有神奇的地方,那么Lock前缀的指令在多核处理器下会发现什么事情了?主要有这两个方面的影           响:排序

  1. 将当前处理器缓存行的数据写回系统内存;
  2. 这个写回内存的操做会使得其余CPU里缓存了该内存地址的数据无效

        为了提升处理速度,处理器不直接和内存进行通讯,而是先将系统内存的数据读到内部缓存(L1,L2或其余)后再进行操做,但操做完不知道什么时候会写到内存。若是对声明了volatile的变量进行写操做,JVM就会向          处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。可是,就算写回到内存,若是其余处理器缓存的值仍是旧的,再执行计算操做就会有问题。因此,在多处理器下,为了保证各个处          理器的缓存是一致的,就会实现缓存一致性协议,每一个处理器经过嗅探在总线上传播的数据来检查本身缓存的值是否是过时了,当处理器发现本身缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成           无效状态,当处理器对这个数据进行修改操做的时候,会从新从系统内存中把数据读处处理器缓存里。所以,通过分析咱们能够得出以下结论:内存

  1. Lock前缀的指令会引发处理器缓存写回内存;
  2. 一个处理器的缓存回写到内存会致使其余处理器的缓存失效;
  3. 当处理器发现本地缓存失效后,就会从内存中重读该变量数据,便可以获取当前最新值。

   有序性:io

       Lock前缀指令实际上至关于一个内存屏障(也成内存栅栏),它确保指令重排序时不会把其后面的指令排到内存屏障以前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它          前面的操做已经所有完成。class

 

理解了volatile就处理上面代码的问题了。

/**
 * @author Lin
 * @Date 2018/2/22.
 */
public class TestVolatile {

    private static volatile boolean isOver = false;

    public static void main(String[] args) {
        new Thread( () -> {while (!isOver){
            System.out.println("111");
        }} ).start();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        isOver = true;
        System.out.println("End");
    }
}
相关文章
相关标签/搜索