Java内存模型(Java Memory Model, JMM)
屏蔽各种硬件和操作系统的内存差异,以实现Java程序在各种平台下都能达到一致的内存访问效果。
主内存和工作内存
- Java内存模型规定所有的变量都存储在主内存(Main Memory)中
- 每条线程有自己的工作内存(Working Memory),线程的工作内存保存了被该线程使用的变量的主内存副本,线程对变量的所有操作必须在工作内存上进行,不可以直接读写主内存上的数据。
- 线程间变量值的传递均通过主内存完成
- 为提高运行速度,虚拟机(或者是硬件、操作系统本身的优化措施)可能会让工作内存优先存储于寄存器和高速缓存中
volatile变量
特征
- 可见性
- 每次使用volatile变量都必须先从主内存刷新最新的值,用于保证能看见其他线程对此变量所做的修改
- 每次修改volatile变量后必须立刻同步回主内存中,用于保证其他线程可以看见自己对此变量所做的修改
- 禁止指令重排序优化
volatile变量不会被指令重排序优化,保证代码的执行顺序与程序顺序相同
性能
- 读操作volatile变量与普通变量性能没有差别;
- 写操作因为volatile变量需要增加一些内存屏障指令保证处理器不会进行重排序,所以性能比普通变量要慢一些。
- 不过volatile变量的开销仍要比锁低。
- 内存屏障(Memory Barrier或Memory Fence),指重排序时不能把后面的指令重排序到内存屏障之前的位置。
long和double的非原子性协定
内存模型允许虚拟机对没有被volatile修饰的64位long型和double型数据的读写操作分为两次32位的操作。这就可能导致在多线程访问的情况下,某条线程看到的数据即不是旧值,也不是被另外的线程更新后的新值,而是一半新值一半旧值。不过,目前的虚拟机大多会实现对long和double型数据的原子性操作,实际开发中不用特别关注这种情况的发生。
先行发生原则(Happens-Before原则)
判断数据是否存在竞争关系,线程是否安全的非常有用的手段
先行发生原则如下图:
如果两个操作满足先行发生原则,或者可以根据先行发生原则推导出来,则两个操作顺序上具有保障;否则,Java虚拟机可以任意对其排序,可能存在竞争关系导致数据不一致性发生,这时线程是不安全的。