参考文章:https://blog.csdn.net/yanshuanche3765/article/details/78405872#commentBox
volatile保证多线程可见性,volatile修饰的变量不会引起上下文切换和调度
cpu缓存,cpu运算速度与内存读写不匹配,因为cpu运算速度比内存读写快的多
从主内存中获取或者写入数据会花费很长时间,现在大多数cpu都不会直接访问内存,而是访问cpu缓存,cpu缓存是cpu与主内存之间的临时存储器,容量小,交换速度快,缓存中的数据是内存中的一小部分数据,是cpu即将访问的。当cpu调用大量数据时候,就先从缓存中读取从而加快读取速度
按照读取顺序与cpu结合的紧密程度,cpu缓存分为
一级缓存:L1位于cpu内核旁边,是与cpu结合最为紧密的cpu缓存
二级缓存:L2分为内部和外部两种芯片,内部芯片二级缓存运行速度与主频相同,外部芯片二级缓存运行速度则只有主频的一半
三级缓存,只有高端的cpu才有
每一级缓存中所存储的数据都是下一级缓存中存储的数据的一部分
cpu要读取一个数据的时候,首先从一级缓存中查找,如果没有就从二级中查找,如果还没有就从三级缓存中或者是内存总进行查找,一般来说,每级缓存的命中率大概有0.8左右,也就是全部数据量的0.8可以在一级缓存中查到,只有剩下的0.2总数据量从二级缓存中或者是三级缓存或者是内存中读取
缓存行:缓存是分line的,一个段对应一个缓存行,是cpu缓存种可分配的最小存储单元,通常是64字节:当cpu看到一条读取内存的指令的时候,会把内存地址传递给一级缓存,一级缓存会检查它是否有这个内存地址对应的缓存段,如果没有就把整个缓存段从内存共或者更高级的缓存种加载进来。
cpu执行计算的过程为:程序和数据被加载到主内存中,指令和数据被加载到cpu缓存中,cpu执行指令将结果写入cpu缓存中,cpu缓存中的数据写回到主内存中,但是这种方式仅限于单核cpu的时候
如果服务器是多核cpu呢,
多核处理器中主内存核处理器一样是分开的,这时候,L3作为统一的高速缓存共享,处理器1拥有自己的L1 L2
这个时候当核0读取了一个字节根据局部性原理,与他相邻的字节同样会被读入核0的缓存中
核3也读取了同样的一个字节,根据局部性原理,与他相邻的字节同样会被读入到核3的数据中
此时,核0和核3的缓存中拥有同样的数据
核0修改了那个字节之后,被修改后那个字节被回写到了核0的缓存中,但是该信息并没有回写到主内存
当核3访问该数据的时候,造成该数据不同步
为了解决这个问题**,当一个cpu修改缓存中的字节的时候,**服务器中其他cpu的会被通知他们的缓存将是为无效,这样核1在修改缓存中的数据的时候,核3会发现自己的缓存中的数据已经无效,核0将自己的写回到主内存中,然后核3将重新读取该数据
将代码转化为汇编指令的时候发现在汇编指令add之前有一个lock指令,lock指令就是关键。
lock指令的作用:在修改内存的时候使用lock前缀指令调用加锁的读修改写操作,保证多处理器系统总处理器之间进行可靠的通讯
1.锁总线,其他cpu对内存的读写请求会被阻塞,直到锁释放,不过实际候来的处理器都采用了缓存缓存代替锁总线,因为总线开销过大,锁总线的时候其他cpu没办法访问内存
2.lock后的写操作会回写已经修改的数据,同时让其他cpu相关缓存行失效,从而重新从内存中加载最新的数据
3.不是内存屏障却能完成内存屏障的功能,阻止屏障两边的指令重排序
嗅探式的缓存一致性协议:所有内存的传输都发生在一条共享的总线上,而所有的处理器都能看到这条总线,缓存本身是独立的,但是内存是共享的。所有的内存访问都要进行仲裁,即同一个指令周期种只有一个cpu缓存可以读写数据。cpu缓存不仅在内存传输的时候与总线打交道,还会不断的在嗅探总线上发生数据交换跟踪其他缓存在做什么,所以当一个cpu缓存代表它所属的处理器读写内存的时候,其他的处理器都会得到通知(主动通知),他们以此使自己的缓存保存同步。只要某个处理器写内存,其他处理器就马上直到这块内存在他们的缓存段种已经失效。。
MESI协议是缓存一致性协议,在MESI协议中每个缓存行有四个状态,Modified修改的,表示这行数据有效,数据被修改了和内存中的数据不一致,数据只存在当前cache中,Exclusive独有的,这行数据有效,数据和内存中的数据一致,数据只存在在本cache中,Shared共享的,这行数据有效,数据和内存中的数据一致,数据存在很多cache中,Invalid这行数据无效,这里的Invalid shared modified都符合我们的嗅探式的缓存一致性协议,但是Exclusive表示独占的,当前数据有效并且和内存中的数据一致,但是只在当前缓存中,Exclusive状态解决了一个cpu缓存在读写内存的之前我们要通知其他处理器这个问题,只有当缓存行处于Exclusive和modified的时候处理器才能写,也就是说只有在这两种状态之下,处理器是独占这个缓存行的,当处理器想写某个缓存行的时候,如果没有独占权就必须先发送一条我要独占权的请求给总线,这个时候会通知处理器把他们拥有同一缓存段的拷贝失效,只要在获得独占权的时候处理器才能修改数据并且此时这个处理器直到这个缓存行只有一份拷贝并且只在它的缓存里,不会有任何冲突,反之如果其他处理器一直想读取这个缓存行(马上就能直到,因为一直在嗅探总线),独占或已修改的缓存行必须要先回到共享状态,如果是已经修改的缓存行,还要先将内容回写到内存中。
volatile变量的读写
工作内存其实就是cpu缓存,当两条线程同时操作主内存中的一个volatile变量时候,A线程写了变量i,此时A线程发出lock指令,发出的lock指令锁总线或者锁缓存行,同时线程b的高速缓存中的缓存行内容失效,线程A想内存中回写最新的i。当线程B读取变量的时候,线程发现对应地址的缓存行被锁了等待锁释放,锁的一致性协议会保证它读取到最新的值。