可见性的保障,得穿透jvm,间接从底层谈起。
对 volatile
的写操作,jvm编译的时候,会在后面加一个 lock
前缀的汇编指令。
lock
指令会引发以下两个动作:
- 引发总线锁定(不肯定,得看缓存行的状态,Exclusive和Modified则不会)
- 强制刷新storeBuffer,到磁盘
《并发编程的艺术》中说,volatile
有 synchronized
的语意,就是因为第一个动作。而保障可见性
的次要起因之一,就是第二个动作。另外一个起因是ESMI协定
,变成Invalid
状态的缓存行,须要从新拉取。
每一个cpu都有一个storeBuffer
,在store的时候,并不会立刻存入缓存,而是先存入storeBuffer,择机再存入缓存。而每个cup的storeBuffer,都是只对本人可见的,其余cpu无从得悉(也就是无奈通过总线获取),这就是造成不可见的本源!造成不可见的本源!造成不可见的本源!
所以,强制刷新下来,就可见了。
过程是这样的:
当一个volatile变量,被批改后,store时会存入storeBuffer。而后,强制把storeBuffer拉到缓存里,再从缓存写进内存里,再从内存写进磁盘里。而后以后的cpu会发信号给总线,告诉其余cpu,这个volatile变量所在行,曾经被改了,须要设置为生效(EMSI协定)。
其余cpu中的线程,要read这个volatile变量时,不得不从新拉数据。首先通过总线,去其余cpu缓存中找。没有的话,就得从内存拉。内存没有,就得从磁盘拉。
因为之前,改volatile变量时,曾经把缓存、内存、主存里的变量更新了。再拉的都是新值。