相关名词解释

我们将会用到几个名词,先来搞清楚这几个名词的含义。
- Java内存模型
Java虚拟机规范中定义的Java内存模型,其目标是屏蔽各种硬件和操作系统的内存访问差异,实现Java程序在各种平台下都能达到一致的内存访问效果,具体体现在其在程序中遵循的各种变量(包含实例字段、静态字段、不变字段和数组对象)的访问规则(可以理解为将变量从内存中取出和存储到内存的底层细节)。Java之所以跨平台正是因为如此。
- 主内存、工作内存
Java虚拟机规定了所有的变量都存储在主内存中,线程在使用这些变量的时候,需要首先从主内存拷贝到工作内存(这个说法不严谨,可以暂时这么认为),使用完毕后,如果对变量进行了修改,需要写回主内存
主内存可以暂时理解为方法区和Java堆空间,可以先看我的[上一篇]博客。工作内存就是线程私有的栈空间和程序计数器区域(实际上对应的应该是计算机CPU工作的高速缓冲区)。
- 指令重排序
Java中指令重排序分为两种:编译期重排序和运行时重排序。其作用是在不改变程序语义的前提下,充分复用寄存器的存储值,尽可能少的读取、存储寄存器。体现在程序的“乱序执行”。我们通常理解的程序时顺序执行的。
例如:int a=0,b=1,c=2;
b+=c;
a=4;
在被执行重排序后执行可能是这样运行的:
int a=0,b=1,c=2;
a=4;
b+=c;
再如,在server模式下的JVM:

boolean isOk;                                           
isOk = true;                            
while(isOk ){                                               
      doSomething();                                                    
}

可能被重排序为:

boolean isOk;                                           
isOk = true;                            
while(true){                                                
      doSomething();                                                    
}

内存间交互操作

我们接着再来关注下变量从主内存读取到工作内存,然后同步回工作内存的细节,这就是主内存与工作内存之间的交互协议。Java内存模型定义了以下8种操作来完成,它们都是原子操作(除了对long和double类型的变量)。
- lock(锁定)
作用于主内存中的变量,它将一个变量标志为一个线程独占的状态。
- unlock(解锁)
作用于主内存中的变量,解除变量的锁定状态,被解除锁定状态的变量才能被其他线程锁定。
- read(读取)
作用于主内存中的变量,它把一个变量的值从主内存中传递到工作内存,以便进行下一步的load操作。
- load(载入)
作用于工作内存中的变量,它把read操作传递来的变量值放到工作内存中的变量副本中。
- use(使用)
作用于工作内存中的变量,这个操作把变量副本中的值传递给执行引擎。当执行需要使用到变量值的字节码指令的时候就会执行这个操作。
- assign(赋值)
作用于工作内存中的变量,接收执行引擎传递过来的值,将其赋给工作内存中的变量。当执行赋值的字节码指令的时候就会执行这个操作。
- store(存储)
作用于工作内存中的变量,它把工作内存中的值传递到主内存中来,以便进行下一步write操作。
- write(写入)
作用于主内存中的变量,它把store传递过来的值放到主内存的变量中。

在将变量从主内存读取到工作内存中,必须顺序执行read、load;要将变量从工作内存同步回主内存中,必须顺序执行store、write。并且这8种操作必须遵循以下规则:
- 1,不允许read和load、store和write操作之一单独出现。即不允许一个变量从主内存被读取了,但是工作内存不接受,或者从工作内存回写了但是主内存不接受。
- 2,不允许一个线程丢弃它最近的一个assign操作,即变量在工作内存被更改后必须同步改更改回主内存。
- 3,工作内存中的变量在没有执行过assign操作时,不允许无意义的同步回主内存。
- 4,在执行use前必须已执行load,在执行store前必须已执行assign。
- 5,一个变量在同一时刻只允许一个线程对其执行lock操作,一个线程可以对同一个变量执行多次lock,但必须执行相同次数的unlock操作才可解锁。
- 6,一个线程在lock一个变量的时候,将会清空工作内存中的此变量的值,执行引擎在use前必须重新read和load。
- 7,线程不允许unlock其他线程的lock操作。并且unlock操作必须是在本线程的lock操作之后。
- 8,在执行unlock之前,必须首先执行了store和write操作。