用来屏蔽各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果。
主要目的是定义程序中各种变量的访问规则,即关注在虚拟机中把变量值存储到内存和从内存中取出变量值的底层细节。
主内存与工作内存
主内存存放所有的变量。
每条线程有自己的工作内存,线程的工作内存中保存了该线程使用的变量的主内存副本,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的数据。
内存间操作
lock
(锁定):作用于主内存的变量,把一个变量标识未一条线程独占的状态。
unlock
(解锁):作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
read
(读取):作用于主内存的变量,把一个主内存变量的值传输到线程的工作内存,以便随后的load操作使用。
load
(载入):作用于工作内存的变量,把read操作从主内存中读取的变量的值放到工作内存的变量副本中。
use
(使用):作用于工作内存中的变量,把工作内存中的一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时就会执行该操作。
assign
(赋值):作用于工作内存的变量,把执行引擎接收的值赋值给工作内存中的变量,每当虚拟机遇到一个给变量赋值的字节码指令时就会执行该操作。
store
(存储):作用于工作内存中的变量,把工作内存中的一个变量的值传递给主内存,以便随后的write操作使用
write
(写入):作用于主内存的变量,把store操作从工作内存中得到的变量的值放入主内存的变量中。
操作规则
1.不允许read和load、store和write操作之一单独出现,即不允许从主内存读取了变量的值但是工作内存不接收的情况,或者不允许从工作内存将变量的值回写到主内存但是主内存不接收的情况。
2.不允许一个线程丢弃最近的assign操作,即变量在工作内存中改变了之后必须把该变化同步回主内存。
3.不允许一个线程回写没有修改的变量到主内存。
4.变量只能在主内存中产生,不允许在工作内存中直接使用一个未被初始化的变量,也就是没有执行load或者assign操作。也就是说在执行use、store之前必须对相同的变量执行了load、assign操作。
5.一个变量在同一时刻只能被一个线程对其进行lock操作,也就是说一个线程一旦对一个变量加锁后,在该线程没有释放掉锁之前,其他线程是不能对其加锁的,但是同一个线程对一个变量加锁后,可以继续加锁,同时在释放锁的时候释放锁次数必须和加锁次数相同。
6.对变量执行lock操作,就会清空工作空间该变量的值,执行引擎使用这个变量之前,需要重新load或者assign操作初始化变量的值。
7.不允许对没有lock的变量执行unlock操作,如果一个变量没有被lock操作,那也不能对其执行unlock操作,当然一个线程也不能对被其他线程lock的变量执行unlock操作。
8.对一个变量执行unlock之前,必须先把变量同步回主内存中,也就是执行store和write操作。
volatile型变量特殊规则
1.每次使用V变量前都必须先从主内存刷新最新的值,用于保证能看见其他线程对变量V所做的修改。
2.每次修改变量V后都必须立刻同步回主内存,用于保证其他线程可以看到自己对变量V所做的修改。
3.volatile修饰的变量不会被指令重排序优化,从而保证代码的执行顺序与程序的顺序相同。有volatile修饰的变量,编译后多一条指令操作,lock...该操作相当于一个内存屏障,指重排序不能把后面的指令重排序到内存屏障之前的位置。
long和double型变量特殊规则
没有被volatile修饰的64位数据的读写操作划分为两次32位的操作进行,即虚拟机可自行选择是否保证64位数据类型的load、store、read、write操作的原子性。“long和double非原子性协定”。
原子性
Java内存模型直接保证的原子性变量操作包括read、load、assign、use、store、write.提供lock、unlock,提供更高层次的monitorenter和monitorexit指令。
可见性
当一个线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的。
有序性
如果在本线程观察,所有的操作都是有序的;如果在一个线程中观察另一个线程,所有的操作都是无序的。
先行发生原则(happens-before)
判断数据是否存在竞争,线程是否安全的的有用手段,解决并发环境下两个操作之间是否可能存在冲突的所有问题。
- 程序次序规则:在一个线程内,按照控制流顺序,书写在签名的操作先行发生于书写后面的操作。
- 管程锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作。
- volatile变量规则:对于一个volatile变量的写操作先行发生于后面对这个变量的读操作。
- 线程启动规则:Thread对象的start()方法先行发生于此线程的每一个动作。
- 线程终止规则:线程中的所有操作都先行发生于对此线程的终止检测。
- 线程中断规则:对现场的interrupt()方法的调用先行发生于被中断线程的代码检测到中断时间的发生。
- 对象终结规则:一个对象的初始化完成,先行发生于它的finalize()方法的开始。
- 传递性:如果操作A先行发生于操作B,操作B先行发生与操作C,那操作A先行发生与操作C。
网友评论