Java 虚拟机
我们都知道 Java 语言的可以跨平台的,这其中的核心是因为存在 Java 虚拟机这个玩意。虚拟机,顾名思义就是虚拟的机器,这不是真实存在的硬件,但是却可以和不同的底层平台进行交互。而且 Java 虚拟机模拟的还比较全面,它想象了自己拥有硬件,处理器,寄存器和堆栈等,还具有相应的指令系统,以此来对接不同的底层操作系统。
Java 内存模型(Java Memory Model)
上次已经说过了底层硬件中内存的相关结构和处理,那同样的对于 Java 虚拟机这个“机器”来说,是不是也应该会有相应的结构呢?因为说到底虚拟机玩的再花,要什么有什么,最终还是要和底层 RAM 进行交互的嘛。
怎么交互?这就是一个大问题,有一群专家就定义了一套规范,定义 Java 内存模型并不是一件容易的事情,这个模型必须定义得足够严谨,才能让 Java 的并发操作不会产生歧义;但是,也必须得足够宽松,使得虚拟机的实现能有足够的自由空间去利用硬件的各种特性(寄存器、高速缓存等)来获取更好的执行速度。
Java 内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样底层细节。此处的变量与 Java 编程时所说的变量不一样,只包括了实例字段、静态字段和构成数组对象的元素,但是不包括局部变量与方法参数,后者是线程私有的,不会被共享。
Java 内存模型中规定了所有的变量都存储在主内存中,每个线程还有自己的工作内存(类比缓存理解),线程的工作内存中保存了该线程使用到主内存中的变量拷贝,线程对变量的所有操作(读取、赋值)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同线程之间无法直接访问对方工作内存中的变量,线程间变量值的传递(通信)均需要在主内存来完成,线程、主内存和工作内存的交互关系如下图所示
这个图和 CPU 与缓存的图非常类似,搞不好 JMM 的构建就是仿照硬件系统来的。同样的道理我们要思考一下在多线程的环境中,JMM 又是如何保证主内存和工作内存中的变量一致性?回忆一下 CPU 是如何保证缓存一致性的,使用 MESI 协议。那在这里呢,Java 内存模型就定义了 8 种操作和 8 个规则。
回头想想,JMM 是一套规则呀,它只会给你定义规范,模型,具体的实现自己玩去!理解这一点很重要。我们来看看它给出了哪些操作和必须满足的规则吧。
lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的 load 动作使用
load(载入):作用于工作内存的变量,把 read 操作从主内存中得到的变量值放入工作内存的变量副本中。
use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的 write 的操作。
write(写入):作用于主内存的变量,它把 store 操作从工作内存中一个变量的值传送到主内存的变量中。