JMM-Java内存模型
JMM试图屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果。
Java内存模型规定所有变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作空间进行,而不能直接读写主存中的变量。
java内存模型。先看下面的图。
这张图就是JMM内存模型的介绍。
当线程需要进行对变量操作时,会从主存中获取数据load到自己的工作内存中,所以说明每个线程进行的变量操作是线程私有的。线程对变量操作完毕后在save到主内存中。
这里提到了save和load操作,那就简单介绍一下吧。
内存间交互操作
-
lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
-
unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
-
read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
-
load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
-
use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
-
assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
-
store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
-
write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。
原子性、可见性、有序性
Java内存模型 是围绕着并发过程如何处理原子性、可见性、‘有序性三个特征来建立的。我们来了解一下这三个特性。
原子性:Java内存模型提供了较大的原子性保证,lock和unlock操作满足这样需求,也提供了字节码指令monitorenter和monitorexit来隐式的使用两个操作,也就是Java代码中的synchronized关键字,所以synchronized的作用包含原子性。
可见性:指当一个线程修改了共享变量的值,其他的线程立刻查看到修改。Java内存模型提供了volatile关键字来在编码过程中实现可见性。Java内存模型在更新共享变量的时,会将新值同步到主内存中,在变量读取前会从主内存刷新读取。
有序性:
在Java中当没有指定排序规则时,虚拟机在会将指令不对结果产生影响的情况下,会进行重新排序。会提升程序的执行效率。
而有序性是JMM是提供的一个特性,有一些有序规则。比如:程序次序规则、管程锁定规则、volatile变量规则等。
这里说下volatile变量规则。
volatile是做了一个禁止指令重排序。加入volatile关键字的时,在编译中多执行了一个“lock addl $ 0x0, (%esp)”操作,这个操作相当于一个内存屏障,重排序时不能把后面的指令重排序到内存屏障之前的位置。但当只有一个CPU访问内存时,并不需要指令屏障的。多个CPU访问时,为了保持一致性,需要内存屏障。