java内存模型(Jave Memory Model, JMM);<br>可参考JSR-133(Java Memory Model and Thread Specification Revision)
@jvm没有直接像c/c++一样直接使用物理硬件和操作系统的内存模型, 增加了跨平台能力;<br><span style="font-size: 18.018px;">@</span>内存模型的定义必须足够严谨, 保证并发内存操作不会产生奇异;<br><span style="font-size: 18.018px;">@</span>但是定义也必须足够宽松, 使得虚拟机的实现有足够的自由空间去利用硬件的各种特性(寄存器/ 高速缓存/ 指令集中某些特有指令)/;
主内存和工作内存
工作内存
每个线程都有自己的工作内存, 工作内存中保存被该内存使用的变量的主内存副本
线程的读写操作必须对工作内存操作, 无法对主内存直接进行读写操作
线程无法直接读写其他线程的工作内存;<br>线程间的变量值的传递需要通过主内存来完成
jvm虚拟机栈的一部分
对应于硬件, 很可能对应了高速缓存甚至寄存器
内存间的交互操作
主要讲了java内存模型的原子性
read/load需要顺序执行;<br>store/write需要顺序执行;<br>但是在指令间可以插入其他指令, 如: read a; read b; load a; use a; load b;
8中原子操作还需要遵从以下规则
不允许read和load / store和write单独出现;<br>即不允许变量从主存读取了工作内存不接受 或者 从工作内存回写了主存不接受的情况
不允许一个线程丢弃它最近的assign操作;<br>即变量在工作内存中改变了之后必须把该变化同步到主存
不允许一个线程无原因地(没有发生过assign操作)把数据从工作内存同步到主存;<br>???防止对主存的无意义刷新更新其他线程已经修改的值(大雾);<br>即对一个变量使用store之前必须经过assign操作
变量只能在主存中"诞生", 不允许在工作内存中使用一个未经初始化(load/assign)的变量;<br>即对一个变量使用use/ store之前必须经过 load/assign操作
一个变量在同一时刻只允许一个线程对其进行lock操作, 但lock可以被同一线程重复执行多次(可重入), 执行多次lock后必须执行相同次数的unlock操作, 变量才能被解锁
如果对一个变量执行lock操作, 将会清空工作内存中此变量的值, 在执行引擎使用该变量前, 需要重新执行load或者assign操作初始化变量的数值
如果一个变量事先没有被lock操作锁定, 那么就不允许对它执行unlock操作;<br>不允许一个线程去unlock一个被其他线程锁住的变量
对一个变量执行unlock操作之前, 必须将该变量同步回主存中(执行store write操作)
这些规则(加上对volatile的一些特殊规定)确定了那些内存访问操作在并发下是安全的
volatile的特殊规定
保证volatile对所有线程的可见性;<br>即 当某线程改变变量值后, 新值对其他线程可立即得知;<br>
可见性不等同于一致性;
volatile在符合以下所有情况时可以保证并发安全:<br>1. 运算结果并不依赖变量的当前值;<br>2. 确保只有一个线程可以修改变量;<br>3.变量不需要与其他的状态变量同时参与不变约束;
volatile变量可以禁止指令冲排序优化
利用了内存屏障, 指令重排序无法越过内存屏障
对long/double的特殊规定
long/double的非原子性协议:<br>允许虚拟机将非volatile修饰的64位数据的读写操作划为两次32位的操作来进行
在目前商用虚拟机中64位数据的读写也是原子性的
原子性/可见性/有序性
原子性:<br>由java内存模型直接保证的原子性操作包括lock/unlock/read/load/use/assign/store/write
可见性:<br>当一个线程修改了共享变量的值, 其他线程能够立即得知这个修改;<br><br>java内存模型通过在变量修改后将新值同步回主存, 在变量读取前从主存刷新变量值这种依赖主存为传递媒介的方式来实现可见性的; 普通变量和volatile变量都是如此;<br><br>不过volatile变量的特殊规定保证了新值能够立即同步到主存, 以及每次使用前立即从主存刷新; 因此可以说volatile保证了多线程操作时变量的可见性, 但普通变量无法保证这一点;<br><br>除了volatile变量外, sync同步代码块和final也可以保证可见性;
有序性:<br>如果在本线程内观察, 所有操作都是有序的; 如果在一个线程中观察另一个线程, 所有操作都是无序的;<br><br>前半句指线程内表现为串行的语义(Whthin-Thread As-If-Serial Semantics);<br>后半句指"指令重排序"现象和"工作内存与主内存同步延迟"现象;<br><br>volatile和sync同步代码块可以保证有序性;
先发性原则
先行发生是java内存模型中定义的两项操作间的偏序关系;<br>如果操作A先行发生于操作B, 实际指 在发生B之前, A产生的影响会被B观察到;
满足以下任意一规则, 则不可指令重拍
程序次序规则
管理锁定规则
volatile变量规则
线程启动规则
线程终止规则
线程中断规则
对象终结规则
传递性
时间先后顺序和先行发生原则之间基本没有太大关系, 所以衡量并发安全问题时只按先行发生原则为准即可;