什么样的数据会存入缓存?
编译器或 CPU 可以明确知晓的, 可能被经常访问的数据. 例如一个在循环体中的变量, 因为这个变量需要经常访问, 如果每次都从主存中拿, 那就太慢了.
缓存一致, 是跟谁一致?
是跟主存一致, 当主存中的对应数据发生变动的时候, CPU 中的缓存也会随之变动. 例如 Cache 中缓存了变量 x 的值, 当主存中的 x 的值变动的时候, Cache 中的值也会刷新."刷新" 可能是将该值标记为 "失效", 下次需要时从主存拉取, 或是直接用新值覆盖.
主存的数据变动是如何被监听到的?
Bus Snooping. 总线窥探, 只要监听总线操作就可知道其他 CPU 对主存的访问情况. 例如监听到 x 变量的写操作, 那就表明 x 变量的值有变动.
高速缓存一致这些操作谁来实现?
底层硬件实现, 现代计算机都有提供此功能.
那这样是不是已经有了可见性?
没有, 当然如果可见性指的是 "当主存值发生变化的时候, Cache 可以看到", 那确实是实现了. 问题是可见性不是这么定义的, 它说的是 "当一个线程对一个变量进行修改时, 其他线程都能够看到".
这两种表述方式有什么不同?
其实就是 "一个线程对变量 A 的修改" ≠ "主存值发生变化". 也就说, 修改变量的值后, 修改的只是缓存中的值, 不会马上写入主存.
为什么不马上写入主存?
因为慢. 所以这个写操作会被重排序到后面, 这个操作还是会执行的, 只是优先级没那么高.
那是不是马上写入主存就能实现可见性呢?
是的, 只要马上写入主存, 由于底层提供 "高速缓存一致性", 所以当内存中的变量发生变更时, 其他 CPU 的缓存也会随之更新. 那这样可见性就有了.
那么如何让它马上写入主存呢?
防止重排序就可以了, 这样写入内存的操作就会被立即执行. 一般就是加内存屏障. synchronized,volatile 都依赖内存屏障.
什么时候需要可见性?
正常的程序变量一般都不需要可见性. 除非这个变量可能被多个线程同时访问, 且你需要用这个变量来协调线程操作. 那这时候这个变量才需要具有可见性. 这时候如果不保证可见性, 就很可能出现奇怪的问题, 即有时可以正确执行, 有时又不行. 为什么呢? 因为有时候, 这些线程刚好在同一 CPU 上执行, 访问的是同一个 Cache, 自然就能得到正确的, 也就是最新的值. 但是, 大多数情况不是这样, 多个线程会在多个 CPU 上执行, 如果不保证可见性, 就可能得到过期的值. 然后你就会很奇怪, 明明看日志打印, 已经改了啊, 为什么其他线程还是没反应过来呢?
插一句
其实缓存一致性和可见性的问题, 都是由多 CPU 的引发的, 就是因为每个 CPU 都有一个 Cache, 所以才有了这一堆的问题.
来源: https://www.cnblogs.com/longfurcat/p/10848925.html