码字不易,欢迎大家转载,烦请注明出处;谢谢配合

主内存与工作内存

Java虚拟机规范试图定义一种Java内存模型(Java Memory Model,JMM) 来屏蔽硬件和操作系统的内存访问的差异,以实现Java在不同平台都能达到一致的内存访问效果。

Java内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存以及从内存中取出变量的底层细节。

Java内存模型规定所有的变量都存储在主内存,每个线程都有自己的工作内存,线程的工作内存中保存着本线程使用变量的主内存数据副本;线程对变量的所有操作(读取,复制)都在工作内存中进行,不能直接在主内存中进行;并且线程之间是相互隔离的,一个线程不能访问其他线程工作内存的变量;线程之间的变量值传递都需要通过主内存来传递;可以参考以下示意图:

image-1655801444728

内存间交互

工作内存与主内存之间具体的交互协议,即具体的主内存复制到工作内存,工作内存同步到主内存的实现细节,Java内存模型定义了8种操作来完成:

  • lock 锁定:作用于主内存的变量,它把变量标识为一个线程独占的状态

  • unlock解锁 :作用于主内存的变量,它把一个锁定状态的线程释放出来,释放出来后的变量才能被其他线程锁定

  • read读取:作用于主内存的变量,它把一个变量的值由主内存传输到工作内存,以便后续load

  • load加载:作用于工作内存的变量,它把read获取到的变量值放入到工作内存的变量副本中

  • use使用:作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用变量的字节码时会执行此操作

  • assign赋值:作用于工作内存的变量,它把从执行引擎获取到的变量值赋值给工作内存,每当虚拟机遇到变量赋值的字节码时会执行此操作

  • store存储:作用于工作内存的变量,它把工作内存的变量值传递给主内存,以便后续write操作使用

  • write写入:作用于主内存的变量,它把store操作从工作内存中获取的值,写入到主内存的变量中

Java内存模型同时还规定了8种操作必须满足以下规则:

1.read-load,store-write 指令,不能单独出现,并且还需要满足顺序性

2.线程变量assign后,必须同步给主内存

3.不允许无原因的进行同步操作,即没有工作内存执行assign操作,而发起向主内存的同步

4.新的变量只能在主内存中诞生

5.一个变量同一时刻,只允许一个线程lock

6.变量没有执行lock,不允许执行unlock

7.执行unlock前,必选先把变量值同步给主内存

8.lock变量时,会清除工作内存变量值,在执行引擎使用变量前要重新load或assign

基于理解难度与严谨性考虑JSR133文档中,已经放弃使用这8种操作去定义Java内存访问协议(描述方式改变了,内存模型没有改变)

happens-before 先行发生原则

happens-before 是Java内存模型保证多线程操作可见性的机制;它具体的表现形式包括我们但是不限于 synchronized、volatile、lock 操作顺序等方面;具体如下:

  • Programm Order Rule程序次序规则 :在一个线程内,按照程序代码顺序,书写在前面的代码先行发生于书写在后面的操作

  • Monitor Lock Rule 管程锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作;这里强调是同一个锁

  • Volatile Veriable RuleVolatile变量规则:对一个Volatile变量的写操作先行发生于后面对该变量的读操作

  • Thread Start Rule线程启动规则:Thread对象的start()先行发生于该线程所有其他操作

  • Thread Terminal Rule线程终止规则:线程所有的操作都先行发生于该线程的终止检测,我们可以通过Thread.join()方法结束,Thread.isAlive()的返回值检测到线程是否已终止

  • Thread Interruption Rule线程中断规则:interrput方法的调用先行发生于该线程代码检测到中断事件发生,可以利用Thread.interrupted()检测是否有中断发生

  • Finalizer Rule对象终结规则:一个对象的初始化先行发生于finlize()方法之前

  • Transtivity传递性:如果A先行发生于B,B先行发生于C;那么A先行发生于C,具有传递性