JAVA虚拟机JVM-3.内存模型

硬件的效率和一致性

Java内存模型和硬件的模型类似。我们先看下硬件的模型。首先强调,计算机处理器的处理运算能力是远远高于存储设备的运算能力的,二者相差几个数量级的运算效率。所以现代计算机引入了一层或者多层读写速度尽可能接近处理器速度的高速缓存来作为内存和处理之间的缓冲。

这样做解决了速度与效率问题,但是出现了缓存一致性的问题。在多路处理器系统中,每个处理器都有自己的告诉缓存,而他们共享一块主存,这种系统成为共享内存多核系统。为了解决这个问题,出现缓存一致性协议,读写时要根据协议进行操作。

Java的内存模型

主内存和工作内存

Java没存模型的主要目的是定义程序中各种变量的访问规则。这里的变量包含实例字段、静态字段以及构成数组对象的元素,但是不包含局部变量表以及方法参数,因为后者是线程私有,不会被共享,不存在竞争关系。

Java内存模型规定了所有的变量都存储于主内存中,每条工作线程都有自己的工作内存,工作线程保存了被该线程使用的变量的主内存副本,线程对变量所有操作都必须在工作内存中完成,不能在主内存完成。不同的线程也不能直接访问对方工作内存中的变量,线程之间传递数据也要通过主内存完成。三者关系如下:

 内存交互的操作

  • lock(锁定):作用于主内存的变量。
  • unlock(解除锁定):作用于主内存的变量。
  • read(读取):作用于主内存的变量,将变量值从主内存传输到线程的工作内存中,以便load动作使用。
  • load(加载):作用于工作内存的变量,将read拿到的变量值保存到工作内存的变量副本中。
  • use(使用):作用于工作内存的变量,将工作内存中的变量值传递给执行引擎,当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
  • assign(赋值):作用于工作内存的变量,将执行银枪接收到的值赋给工作内存的变量,没当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  • store(存储):作用于工作内存的变量,将工作内存中的一个变量值传递到祝内存中,以便与后面write操作使用。
  • write(写入):作用于主内存的变量,将store操作从工作内存中得到的变量值放入主内存的变量中。

操作规则:

  1. 不允许read和load、store和write操作单独出现。
  2. 不允许线程丢弃最近的assign操作,即工作内存中变量改变必须同步会主存。
  3. 不允许无原因(未经过assign操作)的把数据从工作内存同步回主存。
  4. 变量只能诞生于主存中。
  5. 一个变量在同一个时刻只能允许一个线程进行lock操作,但是可以被同一个线程进行多次lock,多次lock后需要进行相同次数的unlock才能进行解锁
  6. 如果对一个对象进行lock操作,那将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load后者assign操作以初始化变量的值。
  7. 如果变量没有被lock,不能进行unlock,也不允许unlock一个被其他线程锁定的变量。
  8. 对一个变量执行unlock之前,必须把此变量同步回祝内存中(执行store和write操作)

Volatile关键字特殊规则

多线程保证其修饰的变量的可见性(但是不代表线程安全,如果保证线程安全还是需要进行Sync操作)

防止指令重新排序优化,普通变量只能保证该方法的执行过程中所有依赖复制结果的地方都能获取到正确的结果,但是不能保证变量赋值的顺序于代码的顺序保持一直,因为同一个线程方法执行过程中无法感知到这点,就是Java内存模型中描述的所谓“线程内表现为串行的语义”。

针对long和double型变量的特殊规则

java内存模型要求所执行的操作都具有原子性,但是对于64为的long和double,定义了“long和double的非原子协定”。具体就是允许将没有被volatile修饰的64位数据的读写操作分为2次32位的操作来进行。

(java内存中32位为一个slot,一个slot认为是原子单位)

原子性、可见性和有序性

Java内存模型的三个特性,原子操作使其拥有原子性;线程修改共享变量的时候其他线程可以感知保证其可见性;如果在本线程内观察,所有的操作都是有序的,如果观察的是另一个线程,那么操作都是无序的。

Java的先行发生原则

  • 程序次序规则:一个线程内,按照顺序,写在前面的操作优先于后面的操作(指的是命令操作,而不是代码操作)
  • 管城锁定规则:unlock操作先行发生于后面对同一个锁的lock操作。
  • volatile变量规则:volatile变量的些操作先行发生于后面对于这个变量的读操作
  • 线程启动原则:Thread的start()方法先行发生于该线程其他的动作。
  • 线程终止规则:线程中所有操作都先行发生于对此线程的终止检测。
  • 线程中断规则:线程interrupt()方法调用先行与发生于被中断线程的代码检测到中断事件的发生。
  • 对象终结规则:一个对象的初始化完成先行与发生于它finalize()方法的开始。
  • 传递性:如果A操作先行与B,B先行与C,那么A一定先行于C。

猜你喜欢

转载自www.cnblogs.com/wangb0402/p/12626832.html