万字详解JVM,让你一文吃透( 八 )


计算机的存储设备和处理器的运算速度差了几个数量级,所以不得不加入一层读写速度尽可能接近处理器运算速度的高速缓存(Cache),作为内存与处理器之间的缓冲:将运算需要的数据复制到缓存中,让运算快速运行 。当运算结束后再从缓存同步回内存,这样处理器就无需等待缓慢的内存读写了 。
基于高速缓存的存储交互很好地解决了处理器与内存的速度矛盾,但是引入了一个新的问题:缓存一致性 。在多处理器系统中,每个处理器都有自己的高速缓存,它们又共享同一主内存 。当多个处理器的运算任务都涉及同一块主内存时,可能导致各自的缓存数据不一致 。
为了解决一致性的问题,需要各个处理器访问缓存时遵循缓存一致性协议 。同时为了使得处理器充分被利用,处理器可能会对输出代码进行乱序执行优化 。Java虚拟机的即时编译器也有类似的指令重排序优化 。
Java 内存模型什么是Java内存模型?Java虚拟机的规范,用来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各个平台下都能达到一致的并发效果 。
Java内存模型的目标?定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出这样的底层细节 。此处的变量包括实例字段、静态字段和构成数组对象的元素,但是不包括局部变量和方法参数,因为这些是线程私有的,不会被共享,所以不存在竞争问题 。
主内存与工作内存所以的变量都存储在主内存,每条线程还有自己的工作内存,保存了被该线程使用到的变量的主内存副本拷贝 。线程对变量的所有操作(读取、赋值)都必须在工作内存中进行,不能直接读写主内存的变量 。不同的线程之间也无法直接访问对方工作内存的变量,线程间变量值的传递需要通过主内存 。

万字详解JVM,让你一文吃透

文章插图
内存间的交互操作一个变量如何从主内存拷贝到工作内存、如何从工作内存同步回主内存,Java内存模型定义了8种操作:
万字详解JVM,让你一文吃透

文章插图
原子性、可见性、有序性
  • 原子性:对基本数据类型的访问和读写是具备原子性的 。对于更大范围的原子性保证,可以使用字节码指令monitorenter和monitorexit来隐式使用lock和unlock操作 。这两个字节码指令反映到Java代码中就是同步块——synchronized关键字 。因此synchronized块之间的操作也具有原子性 。
  • 可见性:当一个线程修改了共享变量的值,其他线程能够立即得知这个修改 。Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取之前从主内存刷新变量值来实现可见性的 。volatile的特殊规则保证了新值能够立即同步到主内存,每次使用前立即从主内存刷新 。synchronized和final也能实现可见性 。final修饰的字段在构造器中一旦被初始化完成,并且构造器没有把this的引用传递出去,那么其他线程中就能看见final字段的值 。
  • 有序性:Java程序的有序性可以总结为一句话,如果在本线程内观察,所有的操作都是有序的(线程内表现为串行的语义);如果在一个线程中观察另一个线程,所有的操作都是无序的(指令重排序和工作内存与主内存同步延迟线性) 。
volatile什么是volatile?关键字volatile是Java虚拟机提供的最轻量级的同步机制 。当一个变量被定义成volatile之后,具备两种特性:
  1. 保证此变量对所有线程的可见性 。当一条线程修改了这个变量的值,新值对于其他线程是可以立即得知的 。而普通变量做不到这一点 。

    经验总结扩展阅读