被volatitle修饰的变量能够保证可见性,不保证原子性,每个线程能够获取该变量的最新值。
实现的机制:在写volatitle变量写到主内存时,指令前会加上lock,该指令有两个影响:
将当前处理器缓存行的数据写回系统内存;
这个写回内存的操作会使得其他CPU里缓存了该内存地址的数据无效。
在多核处理器中,其他线程发现本地缓存失效,就会到主内存重读这个变量,因此在一个volatitle变量发生变化,会发生以下变化:
Lock前缀的指令会引起处理器缓存写回内存;
一个处理器的缓存回写到内存会导致其他处理器的缓存失效;
当处理器发现本地缓存失效后,就会从内存中重读该变量数据,即可以获取当前最新值;
这样针对volatile变量通过这样的机制就使得每个线程都能获得该变量的最新值。
volatitle内存语义实现:JMM在不改变正确语义的情况下,会允许编译器和处理器对指令进行重排,如何实现对重排的控制呢?答案就是内存屏障。
JMM内存屏障:
java编译器会在生成指令系列时在适当的位置会插入内存屏障指令来禁止特定类型的处理器重排序。为了实现volatile的内存语义,JMM会限制特定类型的编译器和处理器重排序,JMM会针对编译器制定volatile重排序规则表:
'NO'表示禁止重排序。为了实现volatile内存语义时,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。对于编译器来说,发现一个最优布置来最小化插入屏障的总数几乎是不可能的,为此,JMM采取了保守策略:
在每个volatile写操作的前面插入一个StoreStore屏障;
在每个volatile写操作的后面插入一个StoreLoad屏障;
在每个volatile读操作的后面插入一个LoadLoad屏障;
在每个volatile读操作的后面插入一个LoadStore屏障。