volitile重排序相关解释
对volitile变量的写入操作之后要保证不能和读之后的读操作重排序。这是内存重排序的一种解决方案happenbeforr中规定的底层通过刚开始讲的内存屏障保证不会重排序。
volitile是通过happenbefore原则保证的。
普通变量的写入和读取再各个线程都有自己的缓存不能够及时同步到主存中,因此多线程下会出现问题,单线程不会出现问题是因为if-else-serial原则保障在单线程环境下即使重排序也不会改变运行的结果;但是它并没有规定多线程环境下。
重排序分类
后来的happenbefore原则提供了方案,底层通过加读写内存屏障来解决重排序的问题。重排序分为三种:
1.内存重排序:storeloadbuffer延迟同步主存
2.编译重排序:编译期间提高效率重排序
3.硬件级别重排序:硬件的指令会重排序两个互不依赖的操作(这是她以为的,其实多线程下虽然各个线程保证了不会影响运行结果但是多线程下不敢保证因为cpu调度是不能够得知的混乱的时间不能提前预知但是可以基于运行时的数据进行优化)
这三个都可以通过内存屏障来解决linux中就是通过这种来保证指令再一定条件下不能够重排序的!
重排序实现方式
本身cpu内核也有三级缓存但是性能还是耗费严重,后来加入了loadbuffer和storebuffer来提高效率但是它们不同与三级缓存本身cpu架构保证的缓存一致性协议(intel为mesi协议)。
这些新加入的buffer是异步同步到主存中的也就是:线程的写入操作会延迟更新到主存中。注意:这里并不是说mesi协议就不管用了他依然管用但是太耗费性能。
为什么呢?
因为都要不能处于同一个缓存行当中才能实现线程安全,对于处于同一个缓存行中的数据,其他线程修改后需要重新读取整个缓存行,一个缓存行的大小!因此在L1的基础上又加入了buffer延迟写入虽然不安全但是效率很高而且大部分情况下稍等一会就能获取到更新的值。(三级缓存是能够保证同步到住内存就是因为缓存一致性协议的作用)。
但是后来发现buffer不能够在用到的时候提交到主存中,因此又出现了load和StoreBuffer。当对一个变量进行读操作时必须保证所有对他的写入操作完成并且同步到主存中,进行写操作也是同样必须保证所有对这个变量的读操作结束。
因此出现了四种协议,LoadStore,LoadLoad,StoreLoad,StoreStore。
也就是说缓存一致性只是保证了同一个缓存行多线程修改保证同步,但是性能损耗严重后来又加入了各种buffer来作为缓存,但是这些新加入的缓存是异步的不是立马同步到主存中的。storbuffer和L1缓存是异步的不能保证同步。