可以聊聊CAS吗?
CAS指令需要有三个操作数,分别是
(1)内存位置(在Java中可以简单地理解为变量的内存地址,用V 表示)
(2)旧的预期值(用A表示)
(3)准备写入的新值(用B表示)。
CAS指令执行时,当且仅当V符合 A时,处理器才会用B更新V的值,否则它就不执行更新。但是,不管是否更新了V的值,都会返回V的旧值,上述的处理过程是一个原子操作,执行期间不会被其他线程中断。
它有什么问题吗?
CAS虽然很高效,但是它也存在三大问题,这里也简单说一下:
(1)ABA问题,如果一个变量V初次读取的时候是A 值,并后来变成了B,然后又变成了A,那么CAS进行检查时会发现值没有发生变化,但是实际上是有变化的。但CAS操作就会误认为它从来没有被改变过。这个漏洞称为CAS操作的“ABA问题”。J.U.C包为了解决这个问题,提供了一个带有标记的原子引用类AtomicStampedReference,它可以通过控制变量值的版本来保证CAS的正确性。好比,“A-B-A”变成了“1A-2B-3A”。不过目前来说这个类处于相当鸡肋的位置,大部分情况下ABA问题不会影响程序并发的正确性,如果需要解决ABA问题,改用传统的互斥同步可能会比原子类更为高效。
(2) 循环时间长开销大。CAS操作如果长时间不成功,会导致其一直自旋,给CPU带来非常大的开销。
(3)只能保证一个共享变量的原子操作。对一个共享变量执行操作时,CAS能够保证原子操作,但是对多个共享变量操作时,CAS是无法保证操作的原子性的。
Java从1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,可以把多个变量放在一个对象里来进行CAS操作。