关于volatile变量的内存可见性,我在JVM群中抛出了一个问题,然后我的一些认知就被颠覆了。
问题
请教一个问题,a,b,c三个变量,其中c是volatile的,a,b是普通变量, a = 1, b = 2, c = 3, c写入之后,a,b的值也会被刷入缓存吗,还是c写入之前所有在cpu缓存的数据都会被刷入内存,还是只刷入和c在同一个缓存行的数据?
一开始,我的认知是后者,只刷入同一个缓存行的数据。
R大问答
你们要按happens before来考虑这种问题,不要整天无谓想着擦车(cache),Java的修正过的内存模型其实基本点很简单,同一线程内的副作用按程序顺序发生,所以a、b、c的赋值如果是在同一线程内按这个顺序写的,实际执行就要按照这个顺序发生(至少表象上要按照这个顺序;在程序无法感知顺序差异时可以作弊)
这样就是a赋值happens before b赋值,b赋值happens before c赋值,而不同线程之间的操作则是没有happens before关系的,除非有volatile或者synchronized等带有跨线程happen before关系的操作。不同线程之间的非volatile、非synchronized操作直接要想有传递的happens before关系的话,中间就肯定得有能产生happens before关系的volatile或者synchronized操作。
什么寄存器啊、缓存啊啥的不必扯进来。
我的反应
哦~~~问号脸。。。
R大继续解释
JVM实现的时候是要把这些概念映射下去的(这里的概念应该就是happens before),但是当你在思考高层程序语义的时候却拿不合适的低层语义去解释就很别扭,映射下去的办法就是先有高层语义,然后看具体硬件上提供了哪些原语,然后再去实现。例如说在SPARC上它默认是TSO(total store order)的,在上面需要手工做的同步操作就很简单。
晓铭大大的总结
JIT的时候会去查操作数的属性,如果是volatile会在读写操作附近生成barrier的中间表示,最终barrier中间表示会变成什么指令,那要根据具体的机器,Memory consistency是一个spec,各种硬件系统包括cache都是实现的细节。
总结
总结起来,我这个问题问的不好,没水准,问题直接从jmm跳到了硬件具体实现,中间还隔着一层硬件的memory model。
所以缓存到底怎么刷,不同的CPU、不同厂商、不同型号实现可能都不一样,一个人想了解所有似乎是个不可能的任务,也不实用,只需要搞清楚JMM层面的东西就OK,管它到底怎么刷。
推广
知识星球可以干什么?
1、【分享】高质量的技术文章
2、【沉淀】「战狼群」高质量问题&解决方案
3、【成长】项目经验,生活随笔,学习心得
4、【复盘】实战经验,故障总结
5、【面经】面试经验分享与总结
6、【推荐】技术书籍,岗位招聘