深入理解Java虚拟机,学习笔记(六)Java内存模型

 2019-12-10 15:58  阅读(899)
文章分类:Java Core

Java内存模型

硬件的效率与一致性

硬件的效率不是每个都像处理器一样高效,所以在处理器和内存之间引入了cache(高速缓存)。
cache的出现同时也增加了计算机系统的复杂度。
因为如果是多核处理器,每个cpu里面都有自己的缓存,当多个缓存访问了同一块内存区域,同时也做了不同的修改,那内存里面该以哪一个缓存数据为主呢。
所以引入了缓存一致性协议。
下面的Java 内存模型,可以理解为在某一特定协议下,对特定的内存或者高速缓存进行读写访问的过程抽象。
20191210001453\_1.png
当然处理器里面为了充分利用处理器的性能会对指令进行乱序执行(out-of-order),会将执行的结果进行重组,最后该结果与顺序执行的结果一致。
jvm中也有类似的执行重排序的功能。

Java 内存模型

首先要纠正一点 Java内存模型与JVM内存模型不一样,JVM内存模型是 方法区、堆、栈那些,而Java内存模型的目的是为了解决多线程对共享内存的读写一致性问题,通过happens-before语义规定了重排序后,程序计算结果与按照控制流(代码)顺序执行结果一致,规定了工作内存和主内存,对volatile型变量赋予了特殊对规则和性质(原子性、可见性、有序性)。

主内存和工作内存

20191210001453\_2.png
1.java内存模型的目标就是定义程序变量的访问规则,即虚拟机对变量的读取细节。
2.为了较好的性能,java内存模型并没有限制执行引擎使用特定的处理器寄存器或者高速缓存对主内存对访问,也没有限制编译器对代码执行顺序对修改。
3.java内存模型规定所有的变量都在主内存里面,所有工作内存里面的变量都是主内存的副本。
4.不能对主内存中的变量直接进行修改
5.线程之间的变量不能直接传递,需要去主内存获取。

内存间交互操作

作用在主内存:
1.lock 2.unlock 3.read 读取主内存 4.write 写入主内存
作用于工作内存:
1.load 从主内存加载 2.store 传递给主内存 3.use 值传递给执行引擎 4.assign 赋值

volatile型变量的规则和性质

可见性

可见行:被volatile修饰的变量后在修改后直接写回到主内存中,而不是对主内存直接进行操作。一般的变量可以联想到之前的TLAB(Thread Local Allocation Buffer)本地线程分配缓冲,当它满的时候会被写会主内存。volatile对其他线程是立即可知的。
但这并不意味这在线程不安全的情况下对volatile变量进行修改就是原子型的。
例如:public static volatile int a = 0;
a++;
上面的a++操作在多线程的情况下是非线程安全的。因为它的步骤还是包括 取变量,变量修改,变量写回主内存。在多线程的情况下可能就是会出现多个线程取了变量,并在紧接着的情况下对变量修改,变量写回主内存。那么有些线程对修改就丢失掉了。
所以使用 volatile 保证以下两个规则可以实现线程安全:
1.运算结果不依赖该变量,或者在单线程下
2.不与多个变量同时成为约束条件。

禁止重排序

在有重排序的情况下,多进程操作,可能会忽略某个线程依赖的共享变量,而直接进行之后的操作。所以volatile为了避免这个错误,设置了内存屏障,对屏障之前对指令可以在不影响结果对情况下重排序,但是不能超过这个屏障。并保证结果会被写回主内存。

原子性、可见性、有序性

java内存模型就是围绕着这3个特性建立的。

原子性

之前的 read load use assign store write 都是原子性的,当然也可以使用synchronized 关键字实现原子性,jvm没有将 lock 和unlock提供给用户,但是提供了更高级的 monitorenter 和monitorexit。

可见性

volatile就是主要的例子。
同时 synchronized 和final 也是
synchronized 是同步后会将结果写回主内存,final 的变量在构造里面就需要被赋值好。

有序性

java语言提供了volatile 和 synchronized关键字来保证了线程之间的有序性

hapens-before

如果程序中变量有依赖之前的变量的,这个先天就具有一定的顺序。不需要额外的手段。

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> 深入理解Java虚拟机,学习笔记(六)Java内存模型

相关推荐