我爱编程

Java volatile关键字

2018-06-11  本文已影响0人  _Once1
volatile.png

内存模型概念

高速缓存存在的意义:临时变量都存在于内存中,当CPU执行程序时,速度是非常快的,但是牵扯到和内存交互,存取数据(耗时),会影响程序的执行速度,因此,必须引入高速缓存。当执行程序时,会先从内存中读取值,然后复制给高速缓存,之后程序执行,并将结果写入高速缓存,最后,再将缓存中的值刷新进入内存。高速缓存的存在,提高了程序的执行速度

引入的问题
多线程程序中,各个线程都有自己的高速缓存,因此当他们读取内存中的同一个值时(即共享变量),无法保证读取进缓存和写入内存时序,即缓存不一致,就会产生错误

如何解决?


三个性质

Person p = new Person()
大致会做三件事:

  1. 给Person的实例分配内存空间
  2. 调用Person()的构造函数,初始化成员字段
  3. 将person对象指向分配的内存空间(此时,person就不是null了)

但是,由于上述乱序的存在,上述过程的2和3顺序是无法保证的
当在多线程情况下,执行顺序是1-3-2时,当执行完1-3后,切换至线程B,此时得到的p并未完全初始化, 在使用时就会有问题,这也是DLC单例在多线程环境下必须加volatile修饰instance的原因
注意:要让程序在多线程情况下正确的执行,那就必须同时满足上述三个条件,若其中一个不满足,就可能会导致运行结果不正确


Java的内存模型

Java中规定,所有的变量都是存在主存中,每一个线程都有自己的工作内存(相当于高速缓存),在程序执行时,线程对变量的所有操作都必须在其工作内存中


volatile

如果一个域声明为volatile,那么编译器和虚拟机就知道该域可能是被另一个线程并发更新的
volatile修饰共享变量的意义:

  1. 保证对该域修改的可见性
  2. 禁止指令进行重排,具体是指:
  • 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
  • 在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行
    简单来说,就是volatile会保证volatile之前的语句一定会在volatile之后的语句之前执行,但是并不能保证前面的语句不会乱序执行,其后的语句不会乱序执行

即是指:

  • 它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;
  • 它会强制将对缓存的修改操作立即写入主存;
  • 如果是写操作,它会导致其他CPU中对应的缓存行无效

使用场景:
由于volatile无法保证原子性,因此,只有在明确可以确保原子性的情况下,可以使用volatile,其效率会高于sychroniezd关键字,例如DCL单例中使用volatile和sychronized确保正确性
假设对于共享变量,除了赋值操作之外不会再有其他操作,那个可以将其声明为volatile


参考:这篇博文 https://www.cnblogs.com/dolphin0520/p/3920373.html

上一篇下一篇

猜你喜欢

热点阅读