Theme NexT works best with JavaScript enabled
0%

volatile

^ _ ^

JMM

Java Memory Model,Java内存模型,本身是一种抽象概念,描述的是一组规则或规范,通过这组规范定义了程序中各个变量的访问方式

JMM关于同步的规定

  1. 线程解锁前,必须把共享变量的值刷新回主内存
  2. 线程加锁前,必须读取主内存的最新值到自己的工作内存
  3. 加锁和解锁是同一把锁

工作内存:线程的私有数据区域(栈空间)
主内存:进程中所有线程的共享区域

线程对主内存中的变量进行读写时,必须在工作内存中进行。首先要将变量从主内存拷贝到自己的工作内存,然后对变量进行操作,操作完成后再将变量写回主内存。

JMM需要保证的特性

  1. 可见性
  2. 原子性
  3. 有序性

volatile

Java虚拟机提供的轻量级同步机制

特点

  • 保证可见性
  • 不保证原子性
  • 禁止指令重排

可见性实现原理
进程是资源分配的基本单位,进程中的变量都存储在主内存中。每个线程使用进程中的变量时,会将变量的值拷贝一份放入自己的工作内存中。但对于不加volatile修饰的变量,一个线程在自己的工作内存中对其进行修改后,不会立即写回主内存中,因此其他线程也就不知道这个变量发生的更改,造成了不可见性。

禁止指令重排实现原理
通过插入Memory Barrier指令(内存屏障)来禁止内存屏障前后的指令执行重排序优化。

  • 在volatile写指令之前,插入StoreStore屏障,禁止上面可能的普通写和下面的volatile写重排序;在volatile写指令之后插入StoreLoad屏障,禁止上面的volatile写和下面可能的读/写进行重排序
  • 在volatile读后面插入LoadLoad屏障,禁止下面所有的普通读操作和上面的volatile读重排序;然后插入LoadStore屏障,禁止下面所有的写操作和上面的volatile读重排序

volatile应用场景

1
2
3
1. memory = allocate() // 分配对象内存空间
2. instance(memory) // 初始化对象
3. instance = memory // 设置instance指向刚分配的内存地址,此时instance != null

由于2和3不存在数据依赖关系,而且无论重排前还是重排后程序的执行结果在单线程中没有改变,因此这种重排序优化是允许的。

1
2
3
1. memory = allocate() // 分配对象内存空间
3. instance = memory // 设置instance指向刚分配的内存地址,此时instance != null,但初始化还没完成
2. instance(memory) // 初始化对象

所以当一条线程访问instance不为null时,由于instance实例未初始化完成,也就造成了线程安全问题。