JAVA内存模型

1.主内存和工作内存

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

JAVA内存模型规定

  • 所有的变量都存储在主内存中
  • 每个线程有自己的工作内存(线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行)
  • 不同线程之间无法直接访问对方工作内存中的变量。线程间变量值的传递靠主内存来完成。


    Image.png

2.内存间的交互操作

主内存和工作内存的交互协议

  • lock(锁定):作用于主内存的变量,它把一个变量标识为一条线程独占的状态。
  • unlock(解锁):作用于主内存的变量,它把一个处理锁定状态的变量释放出来,释放之后的变量才可以被其他线程锁定。
  • read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。
  • load(载入):作用于工作内存的变量,它把read操作从主内存中的得到的变量值放入到工作内存的变量副本中。
  • use(使用):作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时,将会执行这个操作。
  • assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  • store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的write操作使用。
  • write(写入):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。

简略示意图

主内存和工作内存的交互协议.jpg

如果要把一个变量从主内存复制到工作内存,那就要按顺序执行read和load操作。
如果要把变量从工作内存同步到主内存,就要按顺序执行store和write操作。
注意:Java内存模型只要求上述两个操作必须按照顺序执行,但是没有保证必须要连续执行,其中可以插入其他指令。比如:对主内存中的变量a、b进行访问时,可能出现操作顺序是:read a、read b、load b、load a的情况。

Java内存模型规定

  • 不允许read和load、store和write操作之一单独出现。即不允许一个变量从主内存读取了但工作内存不接受。或者从工作内存发起回写了但主内存不接受的情况。
  • 不允许一个线程丢弃它的最近的assign操作,即变量在工作内存中改变了之后必须把该变化同步回主内存。
  • 一个新的变量只能在主内存中诞生。不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即对一个变量实施ues和store操作之前,必须先执行过assign和load操作。
  • 一个变量在同一个时刻只允许一条线程对其进行lock操作。但lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。
  • 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始化变量的值。
  • 如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作。也不允许去unlock一个被其他线程锁定住的变量。
  • 对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store和write操作)。

3.volatile

volatile 特性

  • 保证此变量对所有线程的可见性:当一个线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的。而普通变量不行。普通变量的值发生改变必须要回写到主内存,其他线程再从主内存中对该变量的新值进行读取,新变量的值才会对其他线程可见。
  • 禁止指令重排序。普通的变量仅仅能保证在该方法的执行过程中所有依赖赋值结果的地方都能获取到正确的结果,但是不能保证变量赋值操作顺序与程序代码中的执行顺序一致。
//比如我们代码里面的顺序是这样
a=1+2;
b=2;
c=a+1;
//实际执行的时候,可能进行优化,使b=2提前执行等。

注意:volatile能保证可见性,但是不能保证原子性,对变量执行类似于 a++的操作的时候,还是需要通过加锁来保证原子性。

volatile是怎么保证可见性的?

被volatile修饰的变量,会在每次读取变量前从主内存刷新变量的值,再进行读取。通过依赖主内存作为传递媒介的方式来实现可见性。

你可能感兴趣的:(JAVA内存模型)