JavaCAS深入理解(鼠篇)

先整理下相关的知识吧。

 

关于互斥所、自旋锁

关于常提到的互斥所和自旋锁,在这篇文章上有详细的说明,译文国外的文章,讲的很透彻。

http://ifeve.com/practice-of-using-spinlock-instead-of-mutex/

之前理解synchronized用的就是互斥锁,但其实对于操作系统来说互斥锁和自旋锁已经优化到不能分开讨论的程度了。

互斥锁的问题在于:当线程尝试获取互斥锁的时候,如果获取互斥锁失败,则睡眠;等待其他线程释放锁后被唤醒,那么问题来了。因为线程的睡眠和唤醒需要调用大量的cpu指令,相对时间开销比较大。这样如果一些资源本身被锁定的时间其实很短,甚至比睡眠和唤醒线程的时间还短,那么使用互斥锁就很浪费时间和性能。

自旋锁的问题在于:当线程尝试获取自旋锁的时候,如果获取自旋锁失败,该线程会一直尝试获取自旋锁,直到成功获取自旋锁。那么问题来了。如果某个线程占有自旋锁的时间较长,那么其他尝试获取自旋锁的线程一直在尝试获取,并占用cpu操作时间,那么就很不划算,这个时候让尝试获取自旋锁的线程睡眠就是一个比较好的选择了。

 

分析下来也就是说:当资源占用时间较短的时候,自旋锁是比较优的选择。当资源占用时间较长的时候,互斥锁是比较优的选择。

上边链接文章中也指出,其实现在操作系统的做法通常自旋锁和互斥锁已经混合使用了。文章的结论是,大部分时间就直接使用互斥锁好了,因为现在大多数操作系统在获得互斥锁之前都会自旋一段时间(混合型互斥锁),只有在你觉得某个场景下,使用自旋锁替确实能提高性能,则可以自己尝试使用自旋锁,并且还要考虑单核系统还是多核系统亦或是分布式系统,总之使用之前一定要做好相关的测试工作。

上边整理了下那篇文章中关于互斥锁、自旋锁的内容,比较偏向理论了。

 

关于cpu缓存一致性

缓存一致性参照infoQ的文章;http://www.infoq.com/cn/articles/cache-coherency-primer

关于这个缓存一致性的知识,只作为知晓;

主要的就是MESI协议。详细内容请查看文章,我自己做的总结就是。

首先结构上cpu对内存的操作,在现在操作系统中,cpu不能直接操作内存,而是通过一级缓存、二级缓存甚至三级缓存操作。数据操作是cpu通过缓存操作内存的。而缓存对内存的操作还要通过总线进行传输。所有内存的操作都是通过总线进行的,缓存是独立的,但内存是共享的。所有的内存访问都要经过仲裁(arbitrate):同一个指令周期中,只有一个缓存可以读写内存。

而保障缓存一致性的现代计算机协议大多都是“窥探”协议,还有一种在很多个cpu场景下才用到的基于目录的协议。

窥探协议的原理是:缓存并不是只有在读写操作的时候,才跟总线打交道,而是不停的窥探总线发生的数据交换,跟踪其他缓存在干什么,也就是说当一个缓存通过总线读写内存的时候,其他缓存都会知道,那么就会根据操作的内存区域,检查自己的缓存中是否有该段内存,如果发现已经被修改,就将自己的缓存置为无效。这样就保障了缓存跟内存的实时对应关系。

关于缓存的定律:

基本定律:在任意时刻,任意级别缓存中的缓存段的内容,等同于它对应的内存中的内容。

回写定律:当所有的脏段被回写后,任意级别缓存中的缓存段的内容,等同于它对应的内存中的内容。

MESI定律:在所有的脏缓存段(M状态)被回写后,任意缓存级别的所有缓存段中的内容,和它们对应的内存中的内容一致。此外,在任意时刻,当某个位置的内存被一个处理器加载入独占缓存段时(E状态),那它就不会再出现在其他任何处理器的缓存中。

MESI协议的内容这里不再赘述,文章中有可以查看。

MESI协议规定内存拥有4中状态(衍生协议可能添加但这4种是必须的):

Invalid 失效缓存段、Shared 共享缓存段、Exclusive 独占缓存段、Modified已修改缓存段。如果缓存L需要对某一段内存写操作,而它并没有独占权,那么它需要发出一条“我要独占权”的消息给总线,其他处理器看到后,就会把同段缓存置为无效。而当处理器需要读取某段缓存时,缓存段的状态必须为共享状态,那么其他还没有回写的缓存段就需要先把数据回写到内存,要读取的处理器,才能从缓存中读取相应的缓存段。这样就保证了缓存一致性。

 

下边介绍下java锁升级这块儿东西,然后整理下CAS用到的cpu锁的内容。

 

下面引用下oracle的官方文档:

Fast Locking in the Java HotSpot VM

* 在java HotSpot虚拟机中实现轻量级锁包含几个实用的不变量

   不知道其他的虚拟机是否包含这些不变量

* 简化了偏向锁算法

* 优化了对象锁在线程间的传输

* HotSpot java虚拟机实用一个两个字(two words)的对象头

* Mark word (对象头)

   > 同步,GC和哈希code信息

 

* Class pointer 类指针

 > 对象类型 (Type of object)

> 快速的锁操作拷贝mark word 到当前激活线程的栈的锁记录中

> 锁记录管理跟

 

 

 

 

 

 

posted on 2017-02-09 18:31  aquariusm  阅读(2131)  评论(0编辑  收藏  举报

导航