1、概念
JVM内存模型用于描述在多线程环境下共享变量如何在线程间通信,以保证多线程程序对共享变量操作的有序性。
2、结构
2.1、主内存
存储共享对象实例;线程共享。
2.2、工作内存
存储共享对象实例的拷贝;线程独占。
3、变量操作类型
3.1、read
主内存把变量值从主内存传递到线程的工作内存中。
3.2、load
工作内存把read操作的值赋值给变量副本。
3.3、store
工作内存把修改后的变量值传递到主内存中。
3.4、write
主内存把store操作的值赋值给共享变量。
3.5、use
工作内存把变量副本的值传递给执行引擎用于程序使用。
3.6、assign
工作内存把程序执行后的值赋值给变量副本。
3.7、lock
主内存把变量标记为线程独占状态。
3.8、unlock
主内存释放变量的线程独占状态。
4、特点
4.1、原子性
byte、short、int、float、boolean、char这些基本数据类型的操作是原子性的;long和double的原子性需要根据操作系统位数确定。32位系统下分两次操作,不是原子性;64位系统下操作只需一次,是原子性。
4.2、可见性
当一个线程修改了共享变量的值,其它线程能立即感知到这种变化。volatile、synchronized、final与锁是常用的保证可见性的方式。例如,对于volatile修饰的变量,每当变量的值被修改时,都会立即把修改同步到主内存中,每当变量的值被访问时,也会立即把主内存的值同步到工作内存。
4.3、有序性
在不影响单线程程序执行结果的前提下,JVM为了提高程序运行效率,会按照一定的规则进行指令重排序优化。指令重排序在多线程环境中会导致无序性问题。
4.3.1、Java的有序行为,即先行发生(happens-before)原则:
(1)程序次序
同一个线程内,按照代码顺序,写在前面的操作先行发生于写在后面的操作。
(2)监视器锁定
unlock操作先行发生于后边对同一个锁的lock操作。
(3)volatile变量
对一个volatile变量的写操作先行发生于后边对这个变量的读操作。
(4)传递规则
操作A先行于操作B,操作B先行于操作C,则操作A先行于操作C。
(5)线程启动
线程的start操作先行于线程内的任何操作。
(6)对象终结
对象的初始先行于finalize()方法。
4.3.2、内存屏障
Java编译器在生成字节码时,会在执行指令序列的适当位置插入内存屏障来限制处理器的重排序。
(1)load-load屏障
保证load1数据的载入先行于load2数据的载入。
(2)load-store屏障
保证load1数据的载入先行于store2数据的存储。
(3)store-store屏障
保证store1数据的存储先行于store2数据的存储。
(4)store-load屏障
保证store1数据的存储先行于load2数据的载入。开销最大。