目录
- JVM运行时数据区
- Java内存模型 vs JVM运行时数据区
- 初看java内存模型
- 多线程中的问题
- 从内存结构到内存模型
- JIT编译器
- volatile关键字
- Shared Variables(共享变量)定义
- 对于同步的规则定义
- Happens-before先行发生原则
- final在JMM中的处理
- Word Traring字节处理
- double和long的特殊处理
- 再看Java内存模型
JVM运行时数据区
作用:用来描述一个java进程里面的内存时什么样的情况。
Java内存模型 vs JVM运行时数据区
相关概念:
Java语言规范:用来描述java语言的特性。
java虚拟机规范:用来约束各个虚拟机的实现。
java内存模型:对java语言的描述。
JVM运行时数据区:对Java虚拟机规范的描述。
初看java内存模型
作用:主要用来描述多线程程序的语义(特性),描述多线程程序的规则。
多线程中的问题
- 所见非所得
- 无法肉眼去检测程序的准确性
- 不同的平台有不同的表现
- 错误很难重现
java提出规范说明如何解决这些问题,JVM去实现这些规则。
从内存结构到内存模型
可见性问题
导致问题:写没有写进主内存;读没有从主内存中读取。
- cpu高速缓存和可见性的关系:
虽然被写入缓存,但是另外一个线程可能无法被读到,即为可见性问题。
- 指令重排序
指令重排导致代码结果不可预估,可能会导致可见性问题。
JIT编译器
JIT编译存在指令重排。
volatile关键字
作用:可以保证可见性问题,写完之后,后续的读可以读到最新的数据。
java内存模型(Java语言)对volatile的规则描述:
JVM对volatile关键字的描述:
只会对相关的volatile变量不做重排序,而不是所有的。
最终实现是由JVM来完成的。
Shared Variables(共享变量)定义
Java语言规范描述
堆:广义上的堆,指java语言规范的堆,包括方法区和堆内存,而不是jvm的堆内存。
线程间的操作的定义
感知:一个线程写,可以被另一个线程读到,即为感知。
资源竞争和线程间操作:同时有多个线程对同一个资源进行操作,其中至少有一个是写,则存在资源竞争,出现资源竞争后这样的一个操作称之为线程间操作。
加锁和解锁也是多个线程的操作,会影响其它线程,也属于线程间的操作。
对于同步的规则定义
同步即为可见。
监视器主要针对于Synchronized。
默认值防止读到乱码或其它值。
启动线程 t1.start会修改线程状态的操作,t2会感知。
最后操作 t2线程执行完后,t1线程可见。
总结:不会被缓存,不会做相应的指令重排,保证可见性。
Happens-before先行发生原则
final在JMM中的处理
java内存模型中对final的描述规则。
多线程环境下只有用final修饰才能看到正确的构造版本。
如果没有用final修饰,可能看到的就是默认值。
Word Traring字节处理
不要多线程中对字节数组进行读写操作。
double和long的特殊处理
虽然JVM已经实现原子操作,但是java语言规范建议
再看Java内存模型
总结
java内存模型和JVM内存区域是完全不同的东西,不要混淆。
java内存模型解决java多线程中存在的以上问题。
volatile主要解决读写的可见性问题,禁止缓存和指令重排。
staitc不保证可见性,只是将数据存在方法区,而不是存在堆中。