JVM Volatile缓存一致性协议

java内存操作

Volatile 只能保证可见性与有序性,不能保证原子性。
其实现原理为:内存屏障。
java内存模型定义了8种操作,这8种操作都是原子的,不可再分的:
lock:作用于主内存变量
unlock:作用于主内存变量
read:作用于主内存变量
load:作用于工作内存变量
use:作用于工作内存变量
assign:作用于工作内存变量
store:作用于工作内存变量
write:作用于主内存变量
在这里插入图片描述

对一个变量的操作,read操作一定是在load操作之前,store一定是在write操作之前,且不允许read、load,store、write单独出现。
不允许一个线程丢弃它最近的一次assign操作。
不允许一个线程在未执行assign操作时,把变量从工作内存同步到主内存。
一个变量只能在主内存中“诞生”,不允许在工作内存中直接使用一个未初始化(load或assign)的变量,换句话说,一个变量在执行use操作之前,必须执行load和assign。
一个变量只允许同时被一个线程lock,但是允许该线程多次lock该变量。
对一个变量执行lock操作时,会清空工作内存中该变量的值,在执行引擎使用该变量时,需重新执行load或assign操作以初始化变量的值。
如果一个变量没有被lock,则不能被unlock。
在unlock一个变量前,需要先执行store和write操作,把该变量从工作内存同步到主内存。

volatile内存模型特殊定义

汇编层面,加了volatile关键字的变量,在赋值之后会执行lock addl $0x0,(%rsp)操作,这个操作相当于一个内存屏障。lock前缀的作用是将本处理器的缓存写入内存,该写入动作也会导致别的处理器或者别的内核无效化其缓存,这种操作相当于对缓存中的变量做了一次store和write。

java内存模型中对volatile变量有特殊的定义: 假定T表示一个线程,V和W分别表示两个volatile型变量,那么在进行read、load、use、assign、store和write操作时需要满足如下规则:
·规则1:只有当线程T对变量V执行的前一个动作是load的时候,线程T才能对变量V执行use动作;并且,
只有当线程T对变量V执行的后一个动作是use的时候,线程T才能对变量V执行load动作。线程T对变量
V的use动作可以认为是和线程T对变量V的load、read动作相关联的,必须连续且一起出现。
这条规则要求在工作内存中,每次使用V前都必须先从主内存刷新最新的值,用于保证能看见其 他线程对变量V所做的修改。
·规则2:只有当线程T对变量V执行的前一个动作是assign的时候,线程T才能对变量V执行store动作;并
且,只有当线程T对变量V执行的后一个动作是store的时候,线程T才能对变量V执行assign动作。线程
T对变量V的assign动作可以认为是和线程T对变量V的store、write动作相关联的,必须连续且一起出 现。
这条规则要求在工作内存中,每次修改V后都必须立刻同步回主内存中,用于保证其他线程可以 看到自己对变量V所做的修改。
·规则3:假定动作A是线程T对变量V实施的use或assign动作,假定动作F是和动作A相关联的load或store动
作,假定动作P是和动作F相应的对变量V的read或write动作;与此类似,假定动作B是线程T对变量W
实施的use或assign动作,假定动作G是和动作B相关联的load或store动作,假定动作Q是和动作G相应的
对变量W的read或write动作。如果A先于B,那么P先于Q。
这条规则要求volatile修饰的变量不会被指令重排序优化,从而保证代码的执行顺序与程序的顺序 相同。

规则1和规则2保证了缓存一致性。

指令重排序

所谓的指令重排序是:在硬件层面,处理器允许将多条指令不按顺序分开发送给电路单元进行处理。
lock addl $0x0,(%rsp)指令将修改同步到内存时,意味着修改之前的操作都已经完成。

happens-before

happens-before:A happens-before B就是A先行发生于B(这种说法不是很准确),定义为hb(A, B)。在Java内存模型中,happens-before的意思是前一个操作的结果可以被后续操作获取。
JVM会对代码进行编译优化,会出现指令重排序情况,为了避免编译优化对并发编程安全性的影响,需要happens-before规则定义一些禁止编译优化的场景,保证并发编程的正确性。
happens-before规则:

程序次序规则:在一个线程内一段代码的执行结果是有序的。就是还会指令重排,但是随便它怎么排,结果是按照我们代码的顺序生成的不会变。
管程锁定规则:就是无论是在单线程环境还是多线程环境,对于同一个锁来说,一个线程对这个锁解锁之后,另一个线程获取了这个锁都能看到前一个线程的操作结果!(管程是一种通用的同步原语,synchronized就是管程的实现)
volatile变量规则:就是如果一个线程先去写一个volatile变量,然后一个线程去读这个变量,那么这个写操作的结果一定对读的这个线程可见。
线程启动规则:在主线程A执行过程中,启动子线程B,那么线程A在启动子线程B之前对共享变量的修改结果对线程B可见。
线程终止规则:在主线程A执行过程中,子线程B终止,那么线程B在终止之前对共享变量的修改结果在线程A中可见。也称线程join()规则。
线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程代码检测到中断事件的发生,可以通过Thread.interrupted()检测到是否发生中断。
传递性规则:这个简单的,就是happens-before原则具有传递性,即hb(A, B) , hb(B, C),那么hb(A, C)。
对象终结规则:这个也简单的,就是一个对象的初始化的完成,也就是构造函数执行的结束一定 happens-before它的finalize()方法。

as-if-serial

as-if-serial语义的意思是:不管怎么重排序,单线程程序的执行结果不能被改变。编译器、runtime和处理器都必须遵守as-if-serial语义。所以编译器和处理器不会对存在数据依赖关系的操作做重排序,因为这种重排序会改变执行结果。但是,如果操作之间不存在数据依赖关系,这些操作就可能被编译器和处理器重排序。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值