《深入理解java虚拟机》读书笔记
java内存模型
定义:java虚拟机规范中试图定义一种java内存模型来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能达到一致的访问效果。
1. 主内存与工作内存
java内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量到内存和从内存中取出变量这样的底层细节。java内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,在这里面保存变量的主内存拷贝。线程对变量的所有操作只能在工作内存中进行,而不能直接读写主内存中的变量。

2. 内存间的相互操作
即一个变量如何从主内存拷贝到工作内存、如何从工作内存同步回主内存之类的实现细节。
java虚拟机定义了8种操作来完成,虚拟机实现时必须保证下面的每一种操作都是原子的:
Lock(锁定):作用于主内存中的变量,把一个变量标识为一条线程独占的状态。
Read(读取):作用于主内存中的变量,把一个变量的值从主内存传输到线程的工作内存中。
Load(加载):作用于工作内存中的变量,把read操作从主内存中得到的变量的值放入工作内存的变量副本中。
Use(使用):作用于工作内存中的变量,把工作内存中一个变量的值传递给执行引擎。每当虚拟机遇到一个需要使用到变量的值得字节码指令时将会执行这个操作。
Assign(赋值):作用于工作内存中的变量,把一个从执行引擎接收到的值赋值给工作内存中的变量。
Store(存储):作用于工作内存中的变量,把工作内存中的一个变量的值传送到主内存中。
Write(写入):作用于主内存中的变量,把store操作从工作内存中得到的变量的值放入主内存的变量中。
Unlock(解锁):作用于主内存中的变量,把一个处于锁定状态的变量释放出来,之后可被其它线程锁定。
3. 对于volatile型变量的特殊规则
1. volatile保证变量的可见性和顺序性
当一个变量被定义为volatile变量后,就具有了可见性和顺序性。适合一读多写的场景。volatile关键字是一种最轻量级的同步机制,它的性能要比Synchronized更好,但是它不能够保证操作的原子性,所以不能保证线程安全。
2. volatile可见性的体现
volatile可见性能保证工作内存中对变量的写能立即刷入内存,工作内存中对变量的读会从主内存中取。
- 从原子操作的角度来看:
- 只有read之后才能使用load、load之后必须紧跟着use(这条规定要求在工作内存中,每次使用变量前都必须先从主内存刷新最新的值,用于保证能看见其他线程对变量所做的修改后的值)
- 只有前一个动作是assign时,才能够使用store,write,即把变量的值刷新回主内存。禁止指令重排序的基础是线程对变量V的一整套操作先于对变量M的一整套操作。
- 从工作内存缓存的角度来看:
- 使用volatile关键字的话,当线程2进行修改时,会导致线程1的工作内存中缓存变量stop的缓存行无效
- 由于线程1的工作内存中缓存变量stop的缓存行无效,所以线程1再次读取变量stop的值时会去主存读取。
3. volatile顺序性的体现
lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能:
- 它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;
- 它会强制将对缓存的修改操作立即写入主存;
- 如果是写操作,它会导致其他CPU中对应的缓存行无效。
3.1 volatile使用场景
- 定义一个shutdown变量,用volatile修改,所以对所有线程可见。
- DCL(单例的双重锁定),禁止new Singleton()指令的重排序。可能会出现返回没有被初始化的单例。