volatile

来源:互联网 发布:mac安装win8系统网卡 编辑:程序博客网 时间:2024/06/06 09:31

JMM(Java Memory Model) 主要是为了规定了线程和内存之间的一些关系。系统存在一个主内存(Main Memory),Java中所有变量都存储在主存中,对于所有线程都是共享的。每条线程都有自己的工作内容(Working Memory), 工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主内存完成。



Java内存模型定义了一些列解决可见性(工作内存和主内存交互协议)方法,包括volatile, synchronized, 锁等方式。

如果一个字段被声明成volatile, java线程内存模型确保所有线程看到这个变量的值是一致的。
用javap 命令查看增加volatile前后的字节码没有区别;而用java命令,直接看JIT运行时汇编码,发现多了加了一条lock前缀的指令。

IA-32架构软件开发手册可知:
lock前缀指令在多核处理器下会引发两件事。
1)将当前处理器缓存行的数据会写回到系统内存。
2)这个写回内存的操作会引起在其他CPU里缓存了该内存地址的数据无效。

处理器为了提高处理速度,不直接和内存进行通讯,而是先将系统内存的数据读到内部缓存(L1,L2或其他)后再进行操作,但操作完之后不知道何时会写到内存,如果对声明了Volatile变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。但是就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题,所以在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作的时候,会强制重新从系统内存里把数据读到处理器缓存里。
 
Lock前缀指令会引起处理器缓存回写到内存。Lock前缀指令导致在执行指令期间,声言处理器的 LOCK# 信号。在多处理器环境中,LOCK# 信号确保在声言该信号期间,处理器可以独占使用任何共享内存。(因为它会锁住总线,导致其他CPU不能访问总线,不能访问总线就意味着不能访问系统内存),但是在最近的处理器里,LOCK#信号一般不锁总线,而是锁缓存,毕竟锁总线开销比较大。但在P6和最近的处理器中,如果访问的内存区域已经缓存在处理器内部,则不会声言LOCK#信号。相反地,它会锁定这块内存区域的缓存并回写到内存,并使用缓存一致性机制来确保修改的原子性,此操作被称为“缓存锁定”,缓存一致性机制会阻止同时修改被两个以上处理器缓存的内存区域数据。


注意:
由于使用volatile屏蔽了VM中必要的代码优化,所以在效率上比较低。
0 0