设为首页 加入收藏

TOP

就是要你懂 Java 中 volatile 关键字实现原理(四)
2017-10-22 06:06:45 】 浏览:490
Tags:就是 Java volatile 关键字 实现 原理
个作用:

  1. 锁总线,其它CPU对内存的读写请求都会被阻塞,直到锁释放,不过实际后来的处理器都采用锁缓存替代锁总线,因为锁总线的开销比较大,锁总线期间其他CPU没法访问内存
  2. lock后的写操作会回写已修改的数据,同时让其它CPU相关缓存行失效,从而重新从主存中加载最新的数据
  3. 不是内存屏障却能完成类似内存屏障的功能,阻止屏障两遍的指令重排序

(1)中写了由于效率问题,实际后来的处理器都采用锁缓存来替代锁总线,这种场景下多缓存的数据一致是通过缓存一致性协议来保证的,我们来看一下什么是缓存一致性协议。

缓存一致性协议

讲缓存一致性之前,先说一下缓存行的概念:

  • 缓存是分段(line)的,一个段对应一块存储空间,我们称之为缓存行,它是CPU缓存中可分配的最小存储单元,大小32字节、64字节、128字节不等,这与CPU架构有关,通常来说是64字节。当CPU看到一条读取内存的指令时,它会把内存地址传递给一级数据缓存,一级数据缓存会检查它是否有这个内存地址对应的缓存段,如果没有就把整个缓存段从内存(或更高一级的缓存)中加载进来。注意,这里说的是一次加载整个缓存段,这就是上面提过的局部性原理

上面说了,LOCK#会锁总线,实际上这不现实,因为锁总线效率太低了。因此最好能做到:使用多组缓存,但是它们的行为看起来只有一组缓存那样。缓存一致性协议就是为了做到这一点而设计的,就像名称所暗示的那样,这类协议就是要使多组缓存的内容保持一致。

缓存一致性协议有多种,但是日常处理的大多数计算机设备都属于”嗅探(snooping)”协议,它的基本思想是:

所有内存的传输都发生在一条共享的总线上,而所有的处理器都能看到这条总线:缓存本身是独立的,但是内存是共享资源,所有的内存访问都要经过仲裁(同一个指令周期中,只有一个CPU缓存可以读写内存)。

CPU缓存不仅仅在做内存传输的时候才与总线打交道,而是不停在嗅探总线上发生的数据交换,跟踪其他缓存在做什么。所以当一个缓存代表它所属的处理器去读写内存时,其它处理器都会得到通知,它们以此来使自己的缓存保持同步。只要某个处理器一写内存,其它处理器马上知道这块内存在它们的缓存段中已失效。
MESI协议是当前最主流的缓存一致性协议,在MESI协议中,每个缓存行有4个状态,可用2个bit表示,它们分别是:

这里的I、S和M状态已经有了对应的概念:失效/未载入、干净以及脏的缓存段。所以这里新的知识点只有E状态,代表独占式访问,这个状态解决了”在我们开始修改某块内存之前,我们需要告诉其它处理器”这一问题:只有当缓存行处于E或者M状态时,处理器才能去写它,也就是说只有在这两种状态下,处理器是独占这个缓存行的。当处理器想写某个缓存行时,如果它没有独占权,它必须先发送一条”我要独占权”的请求给总线,这会通知其它处理器把它们拥有的同一缓存段的拷贝失效(如果有)。只有在获得独占权后,处理器才能开始修改数据—-并且此时这个处理器知道,这个缓存行只有一份拷贝,在我自己的缓存里,所以不会有任何冲突。

反之,如果有其它处理器想读取这个缓存行(马上能知道,因为一直在嗅探总线),独占或已修改的缓存行必须先回到”共享”状态。如果是已修改的缓存行,那么还要先把内容回写到内存中。

由lock指令回看volatile变量读写

相信有了上面对于lock的解释,volatile关键字的实现原理应该是一目了然了。首先看一张图:

工作内存Work Memory其实就是对CPU寄存器和高速缓存的抽象,或者说每个线程的工作内存也可以简单理解为CPU寄存器和高速缓存。

那么当写两条线程Thread-A与Threab-B同时操作主存中的一个volatile变量i时,Thread-A写了变量i,那么:

  • Thread-A发出LOCK#指令
  • 发出的LOCK#指令锁总线(或锁缓存行),同时让Thread-B高速缓存中的缓存行内容失效
  • Thread-A向主存回写最新修改的i

Thread-B读取变量i,那么:

  • Thread-B发现对应地址的缓存行被锁了,等待锁的释放,缓存一致性协议会保证它读取到最新的值

由此可以看出,volatile关键字的读和普通变量的读取相比基本没差别,差别主要还是在变量的写操作上。

后记

之前对于volatile关键字的作用我个人还有一些会混淆的误区,在深入理解volatile关键字的作用之后,感觉对volatile的理解深了许多。相信看到文章这里的你,只要肯想、肯研究,一定会和我一样有恍然大悟、茅塞顿开的感觉^_^

参考资料

首页 上一页 1 2 3 4 下一页 尾页 4/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Redis 简述 下一篇基于 ZooKeeper 的分布式锁和队列

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目