`

volatile关键字作用

    博客分类:
  • java
阅读更多

一,简介

       jdk中提供了volatile关键字,用于修饰变量。提供了两层语义:

          语义一:保证共享变量内存的可见性(并不能保证操作的原子性)。

         语义二:禁用指令的重排序。

二,内存可见性原理分析

      在Java内存模型中,内存分为主内存(堆内存)和工作内存(栈内存)两个部分,其中主内存是所有线程所共享的,而工作内存则是每个线程分配一份,各线程的工作内存彼此独立、互不可见,在线程启动的时候,虚拟机为 每个线程分配一块工作内存,不仅包含了线程内部定义的局部变量,还包含了线程所需要使用的共享变量(非线程内构造的对象)的副本,即为了提高执行效率,读取副本比直接读取主内存更快,栈是连续的小空间、顺序入栈出栈,而堆是不连续的大空间,所以在栈中寻址的速度比堆要快很多)。各工作内存之间数据的交换通过主内存来进行的,如下图:

    共享变量在工作内存中发生变化了之后,必须要写回到主内存中(迟早要写但并非马上回),但对于volatile关键字修饰的变量则要求工作内存中发生变化之后,必须马上回到主内存,而线程每次要使用volatile修饰的共享变量时,都会直接到主内存中获取最新的值(而不是读取工作内存中的副本)。从而实现所有线程对该共享变量变化的可见性。

    volatile仅能保证变量的可见性,并不能保证操作的原子性:

   

    假如线程A在做了i+1,但未赋值的时候,线程B就开始读取i,那么当线程A赋值i=1,并立即回写到主内存,而此时线程B已经不再需要i的值了,而是直接交给处理器去做+1的操作,于是当线程B执行完并回写到主内存,i的值仍然是1,而不是预期的2。
三,禁用指令重排序
    synchronized、Lock以及volatile修饰符都可以禁用指令的重排序。
    单例模式中双重检测机制:
   
         private static volatile StoreKeeper instance = null;
         
         private StoreKeeper(){//对象初始化
         }
         
         public static StoreKeeper getInstance(){
              if(null == instance){
                   synchronized (StoreKeeper.class)
                {
                    if(null == instance){
                        instance = new StoreKeeper();
                    }
                }
              }
              return instance;
         }
 
    问题原因:
       当a线程进入getinstance()方法,判断instance是null,然后执行instance = new StoreKeeper();但是实例化一个对象内部是很复杂的,有好多动作:
      1.给这个对象分配空间
      2.执行构造函数给成员赋值
      3.将这个对象空间地址赋给instance变量
 
     而根据java内存模型,在单个线程中,只要重排序不会对结果产生影响,那么就不能保证其中的操作一定按照程序写定的顺序执行---即使重排序对于其他线程来说会产生明显的影响,java存储模型允许编译器重排序操作,在寄存器中缓存数值,还允许cpu重排序,并在处理器特有的缓存中缓存数值,那么就是说线程a去实例化这个对象的时候可能是按照1-2-3的顺序执行,也可能是1-3-2的顺序执行
 
      可能原因:如果是按照1-3-2执行顺序,那么当a线程执行完3这步的时候,cpu调度,这个时候b线程进来,判断instance是否为null,而此时instance肯定不为null,所以b线程就直接返回了这个对象
但是此时这个对象的所有成员变量还没有初始化成指定的值呢,因为线程a还没有来得及给成员变量赋值
构造方法还没有执行完,当b线程调用这个对象进行操作会产生无法预计的后果。
 
    所以修改的方法是增加volatile关键字:private static volatile StoreKeeper instance;
 

 

 

 

  • 大小: 11.7 KB
  • 大小: 22.3 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics