java主要有两套内存模型,一套是JVM对Java内存模型的实现、另一套是硬件内存架构。其中,Java内存模型由“线程栈区”和“堆区”组成,是虚拟存在的,而硬件内存架构由“CPU寄存器(储存器)”、“CPU缓存”、“主存”三者构成,是真实存在的。
这两套模型的关系如下(左边是Java内存模型、右边是硬件内存模型):
可以发现,两套模型的对应关系相当复杂,即“线程栈区”和“堆区”中的数据可能存在于“CPU寄存器(储存器)”、“CPU缓存”、“主存”三者中的任一中。
当读取volatile和synchronized修饰的数据时,都必须去“主存”中去读取,然后在“CPU缓存”中操作。而修改volatile和synchronized修饰的数据时,则会先去“主存”中读取数据,然后在“CPU缓存”中完成操作后立即把修改后的值返回给“主存”中,二者的区别在于:当并行访问来修改同一数据时,volatile不能保证数据的一致性,而synchronized可以。例如:
如下图所示,线程A和线程B共享一个对象obj。假设线程A从主存读取Obj.count变量到自己的CPU缓存,同时,线程B也读取了Obj.count变量到它的CPU缓存,并且这两个线程都对Obj.count做了加1操作。此时,Obj.count加1操作被执行了两次,不过都在不同的CPU缓存中。
如果这两个加1操作是串行执行的,那么Obj.count变量便会在原始值上加2,最终主存中的Obj.count的值会是3。然而下图中两个加1操作是并行的,不管是线程A还是线程B先flush计算结果到主存,最终主存中的Obj.count只会增加1次变成2,尽管一共有两次加1操作。
volatile和static的功能类似,其区别在于,volatile修饰的变量在修改后会立即同步到“主存”中,而static不会立即同步到“主存”中。
参考文档 :
1.https://blog.csdn.net/suifeng3051/article/details/52611310
2.https://blog.csdn.net/suifeng3051/article/details/52611233
3.https://www.zhihu.com/question/41579791