CAS概念

CAS:Compare And Swap,比较并交换。java.util.concurrent包完全是建立于CAS机制之上的。

CAS原理

Java CAS是通过调用Unsafe的native方法,再由C程序调用CPU底层命令实现的。

下面是对应于intel x86处理器的源代码的片段:

// Adding a lock prefix to an instruction on MP machine
// VC++ doesn't like the lock prefix to be on a single line
// so we can't insert a label after the lock prefix.
// By emitting a lock prefix, we can define a label after it.
#define LOCK_IF_MP(mp) __asm cmp mp, 0 \
__asm je L0 \
__asm _emit 0xF0 \
__asm L0: inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) {
// alternative for InterlockedCompareExchange
int mp = os::is_MP();
__asm {
mov edx, dest
mov ecx, exchange_value
mov eax, compare_value
LOCK_IF_MP(mp)
cmpxchg dword ptr [edx], ecx
}
}

如上面源代码所示,程序会根据当前处理器的类型来决定是否为cmpxchg指令添加lock前缀。如果程序是在多处理器上运行,就为cmpxchg指令加上lock前缀(lock cmpxchg)。反之,如果程序是在单处理器上运行,就省略lock前缀(单处理器自身会维护单处理器内的顺序一致性,不需要lock前缀提供的内存屏障效果)。

intel的手册对lock前缀的说明如下:

  1. 确保对内存的读-改-写操作原子执行。
  2. 禁止该指令与之前和之后的读和写指令重排序。
  3. 把写缓冲区中的所有数据刷新到内存中。

那么,cpu是如何来确保读、改、写操作原子性的呢?

  • Bus Locking(总线锁)

在x86 平台上,CPU提供了在指令执行期间对总线加锁的手段。CPU芯片上有一条引线#HLOCK pin,如果汇编语言的程序中在一条指令前面加上前缀"LOCK",经过汇编以后的机器代码就使CPU在执行这条指令的时候把#HLOCK pin的电位拉低,持续到这条指令结束时放开,从而把总线锁住,这样同一总线上别的CPU就暂时不能通过总线访问内存了,保证了这条指令在多处理器环境中可以独占内存。(这种方式性能会比较差,所以后来有了缓存一致性方案)

  • 缓存一致性

  MESI是保持一致性的协议,实现原理是在CPU缓存中记一个标记位,标记位有四种状态:

  M: Modify,只在本CPU中有缓存数据,而其他CPU中没有。同时其状态相对于内存中的值来说,是已经被修改的,且没有更新到内存中。

  E: Exclusive,只有在本CPU中有缓存,且其数据没有修改,即与内存中一致。

  S: Share,数据在多个CPU中都有缓存,且与内存一致。

  I: Invalid,本CPU中的这份缓存已经无效。

     该协议约定的缓存上对应的监听:

  1. 一个处于M状态的缓存行,必须时刻监听所有试图读取该缓存行对应的主存地址的操作,如果监听到,则必须在此操作执行前把其缓存行中的数据写回CPU。
  2. 一个处于E状态的缓存行,必须时刻监听其他试图读取该缓存行对应的主存地址的操作,如果监听到,则必须把其缓存行状态设置为S。
  3. 一个处于S状态的缓存行,必须时刻监听使该缓存行无效或者独享该缓存行的请求,如果监听到,则必须把其缓存行状态设置为I。

  当CPU需要读取数据时,如果其缓存行的状态是I的,则需要从内存中读取,并把自己状态变成S,如果不是I,则可以直接读取缓存中的值,但在此之前,必须要等待其他CPU的监听结果,如其他CPU也有该数据的缓存且状态是M,则需要等待其把缓存更新到内存之后,再读取。

  当CPU需要写数据时,只有在其缓存行是M或者E的时候才能执行,否则需要发出特殊的RFO指令(Read Or Ownership,这是一种总线事务),通知其他CPU置缓存无效(I),这种情况下会性能开销是相对较大的。在写入完成后,修改其缓存状态为M。

  但是有两种情况下处理器不会使用缓存锁定。第一种情况是:当操作的数据不能被缓存在处理器内部,或操作的数据跨多个缓存行(cache line),则处理器会调用总线锁定。第二种情况是:有些处理器不支持缓存锁定。对于Inter486和奔腾处理器,就算锁定的内存区域在处理器的缓存行中也会调用总线锁定。

CAS缺点

  CAS虽然很高效的解决原子操作,但是CAS仍然存在三大问题。

  1. ABA问题

    因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。

  2. 自旋开销大

    自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。jdk1.8好像已经不是采用自旋的方式了,但是据说效率也没有得到多大提高。这个可以自己去测试一下。

  3. 只能保证一个共享变量的原子操作

    当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。

  

J.U.C Atomic(一)CAS原理的更多相关文章

  1. JAVA CAS原理深度分析-转载

    参考文档: http://www.blogjava.net/xylz/archive/2010/07/04/325206.html http://blog.hesey.net/2011/09/reso ...

  2. JAVA CAS原理

    转自: http://blog.csdn.net/hsuxu/article/details/9467651 CAS CAS: Compare and Swap java.util.concurren ...

  3. 【转】JAVA CAS原理深度分析

    java.util.concurrent包完全建立在CAS之上的,没有CAS就不会有此包.可见CAS的重要性. CAS CAS:Compare and Swap, 翻译成比较并交换. java.uti ...

  4. JAVA CAS原理深度分析

    参考文档: http://www.blogjava.net/xylz/archive/2010/07/04/325206.html http://blog.hesey.net/2011/09/reso ...

  5. JAVA CAS原理深度分析(转)

    看了一堆文章,终于把JAVA CAS的原理深入分析清楚了. 感谢GOOGLE强大的搜索,借此挖苦下百度,依靠百度什么都学习不到! 参考文档: http://www.blogjava.net/xylz/ ...

  6. Java中的CAS原理

    前言:在对AQS框架进行分析的过程中发现了很多CAS操作,因此有必要对CAS进行一个梳理,也便更清楚的了解其原理. 1.CAS是什么 CAS,是compare and swap的缩写,中文含义:比较交 ...

  7. JAVA CAS原理浅谈

    java.util.concurrent包完全建立在CAS之上的,没有CAS就不会有此包.可见CAS的重要性. CAS CAS:Compare and Swap, 翻译成比较并交换. java.uti ...

  8. java多线程之CAS原理

    前言 在Java并发包中有这样一个包,java.util.concurrent.atomic,该包是对Java部分数据类型的原子封装,在原有数据类型的基础上,提供了原子性的操作方法,保证了线程安全.下 ...

  9. 转:JAVA CAS原理深度分析

    看了一堆文章,终于把Java CAS的原理深入分析清楚了. 感谢GOOGLE强大的搜索,借此挖苦下百度,依靠百度什么都学习不到! 参考文档: http://www.blogjava.net/xylz/ ...

随机推荐

  1. 专题实验 SQL

    merge merge into copy_emp ce using emp e on (ce.empno = e.empno) when matched then update set ename ...

  2. 学习记录jQuery的animate函数

    很久之前就对jQuery animate的实现非常感兴趣,不过前段时间很忙,直到前几天端午假期才有时间去研究. jQuery.animate的每种动画过渡效果都是通过easing函数实现的.jQuer ...

  3. 【BZOJ】1655: [Usaco2006 Jan] Dollar Dayz 奶牛商店(背包+高精度)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1655 背包就没什么好说的了,裸的完全背包.. 但是我一开始交开了ull都wa了T_T.. 精度太大. ...

  4. 子级用css float浮动 而父级div没高度不能自适应高度

    子级对象使用css float浮动 而父级div不能自适应高度. 对父级div标签闭合</div>前加一个clear清除浮动对象. <!DOCTYPE html> <ht ...

  5. 通过公网IP主机建立ssh隧道

    环境描述 hostA: 有公网IP的linux主机 hostB: 私有路由器后端无公网IPlinux主机,能够ssh连接到hostA hostC: 个人pc机 隧道创建步骤 step1 在hostB上 ...

  6. 10条建议帮助你创建更好的jQuery插件

    本文总结了帮助你创建更好jQuery插件的10条建议.分享给大家供大家参考.具体说明如下: 在开发过很多 jQuery 插件以后,我慢慢的摸索出了一套开发jQuery插件比较标准的结构和模式.这样我就 ...

  7. MathType怎么编辑双箭头

    很多的数学相关工作者在写文章或论文的时候常常会用到数学公式编辑器.MathType就是一款深受大家欢迎的公式编辑器.很多的用户在使用过程中会用到双箭头符号来表示推理过程,但是怎么编辑又不知道,下面本教 ...

  8. hdu 2821(dfs)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2821 思路:一开始的时候没注意到,必须从map[i][j]==0的位置开始,然后就是dfs了,回溯的时 ...

  9. 简易博客开发(8)----django1.9 博客部署到pythonanywhere上

    准备工作 首先需要注册一下,pythonanywhere的免费账户有一定的限制,只能创建一个web app,不能绑定独立域名,不能通过ssh连接,不过只是搭一个project也是够用了. 注册成功之后 ...

  10. C语言关系运算符

    在上节<C语言if else语句>中看到,if 的判断条件中使用了<=.>.!=等符号,它们专门用在判断条件中,让程序决定下一步的操作,称为关系运算符(Relational O ...