JMM-Java内存模型
Java内存模型(Java Memory Model)是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。
Java程序都是运行在JVM(Java虚拟机)上面的,Java内存模型规定了所有的变量都存储在主内存
中,每条线程还有自己的工作内存
,线程的工作内存中保存了该线程中是用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。
JMM规定了内存主要划分为主内存
和工作内存
两种。此处的主内存和工作内存跟JVM内存划分(堆、栈、方法区)是在不同的层次上进行的,如果非要对应起来,主内存对应的是Java堆
中的对象实例部分,工作内存对应的是栈
中的部分区域,从更底层的来说,主内存对应的是硬件的物理内存
,工作内存对应的是寄存器和高速缓存
。
而JMM就作用于工作内存和主存之间数据同步过程。他规定了如何做数据同步以及什么时候做数据同步。
JMM内存交互操作
内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double
和long
类型的变量来说,load
、store
、read
和write
操作在某些平台上允许例外)
8种内存交互操作
lock
(锁定):作用于主内存的变量,把一个变量标识为线程独占状态unlock
(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定read
(读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用load
(载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中use
(使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令assign
(赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中store
(存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用write
(写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中
交互规则
read
和load
、store
和write
必须一起使用,不允许单一出现- 不允许线程丢弃最近的
assign
操作,即工作变量数据改变之后必须告知主存 - 不允许一个线程将没有
assign
的数据从工作内存同步回主内存 - 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是对变量实施
use
、store
操作之前,必须经过assign
和load
操作 - 一个变量同一时间只有一个线程能对其进行
lock
。多次lock
后,必须执行相同次数的unlock
才能解锁 - 如果对一个变量进行
lock
操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load
或assign
操作初始化变量的值 - 如果一个变量没有被
lock
,就不能对其进行unlock
操作。也不能unlock
一个被其他线程锁住的变量 - 对一个变量进行
unlock
操作之前,必须把此变量同步回主内存
栗子
public class VolatileDemo {
private static volatile boolean initFlag = false;
public static void prepareData(){
System.out.println("prepareding data...");
initFlag = true;
System.out.println("prepared end...");
}
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("waiting data ....");
while (!initFlag){
}
System.out.println("===========success");
}
}).start();
Thread.sleep(2000);
new Thread(new Runnable() {
@Override
public void run() {
prepareData();
}
}).start();
}
}
内存操作:
Happen-Before(先行发生规则)
Happen-Before的规则:
- 程序次序规则(Program Order Rule):在一个线程内,程序的执行规则跟程序的书写规则是一致的,从上往下执行。
- 管程锁定规则(Monitor Lock Rule):一个Unlock的操作肯定先于下一次Lock的操作。这里必须是同一个锁。同理我们可以认为在synchronized同步同一个锁的时候,锁内先行执行的代码,对后续同步该锁的线程来说是完全可见的。
- volatile变量规则(volatile Variable Rule):对同一个volatile的变量,先行发生的写操作,肯定早于后续发生的读操作
- 线程启动规则(Thread Start Rule):Thread对象的start()方法先行发生于此线程的每一个动作
- 线程中止规则(Thread Termination Rule):Thread对象的中止检测(如:Thread.join(),Thread.isAlive()等)操作,必行晚于线程中所有操作
- 线程中断规则(Thread Interruption Rule):对线程的interruption()调用,先于被调用的线程检测中断事件(Thread.interrupted())的发生
- 对象中止规则(Finalizer Rule):一个对象的初始化方法先于一个方法执行Finalizer()方法
- 传递性(Transitivity):如果操作A先于操作B、操作B先于操作C,则操作A先于操作C