【杂谈】高速缓存一致性与可见性

什么样的数据会存入缓存?缓存

编译器或CPU能够明确知晓的,可能被常常访问的数据。例如一个在循环体中的变量,由于这个变量须要常常访问,若是每次都从主存中拿,那就太慢了。oop

缓存一致,是跟谁一致?线程

是跟主存一致,当主存中的对应数据发生变更的时候,CPU中的缓存也会随之变更。例如Cache中缓存了变量x的值,当主存中的x的值变更的时候,Cache中的值也会刷新。“刷新”多是将该值标记为“失效”,下次须要时从主存拉取,或是直接用新值覆盖。日志

主存的数据变更是如何被监听到的?排序

Bus Snooping。总线窥探,只要监听总线操做就可知道其余CPU对主存的访问状况。例如监听到x变量的写操做,那就代表x变量的值有变更。内存

高速缓存一致这些操做谁来实现?编译器

底层硬件实现,现代计算机都有提供此功能。编译

那这样是否是已经有了可见性?变量

没有,固然若是可见性指的是“当主存值发生变化的时候,Cache能够看到”,那确实是实现了。问题是可见性不是这么定义的,它说的是“当一个线程对一个变量进行修改时,其余线程都可以看到”。循环

这两种表述方式有什么不一样?

其实就是“一个线程对变量A的修改” ≠ “主存值发生变化”。也就说,修改变量的值后,修改的只是缓存中的值,不会立刻写入主存。

为何不立刻写入主存?

由于慢。因此这个写操做会被重排序到后面,这个操做仍是会执行的,只是优先级没那么高。

那是否是立刻写入主存就能实现可见性呢?

是的,只要立刻写入主存,因为底层提供“高速缓存一致性”,因此当内存中的变量发生变动时,其余CPU的缓存也会随之更新。那这样可见性就有了。

那么如何让它立刻写入主存呢?

防止重排序就能够了,这样写入内存的操做就会被当即执行。通常就是加内存屏障。synchronized、volatile都依赖内存屏障。

何时须要可见性?

正常的程序变量通常都不须要可见性。除非这个变量可能被多个线程同时访问,且你须要用这个变量来协调线程操做。那这时候这个变量才须要具备可见性。这时候若是不保证可见性,就极可能出现奇怪的问题,即有时能够正确执行,有时又不行。为何呢?由于有时候,这些线程恰好在同一CPU上执行,访问的是同一个Cache,天然就能获得正确的,也就是最新的值。可是,大多数状况不是这样,多个线程会在多个CPU上执行,若是不保证可见性,就可能获得过时的值。而后你就会很奇怪,明明看日志打印,已经改了啊,为何其余线程仍是没反应过来呢?

插一句

其实缓存一致性和可见性的问题,都是由多CPU的引起的,就是由于每一个CPU都有一个Cache,因此才有了这一堆的问题。

相关文章
相关标签/搜索