一、CAS
CAS 就是 Compare and Swap (比较并操作) 的意思。很多的 CPU 直接支持 CAS 指令。
CAS 是一项乐观锁技术,当多个线程尝试使用 CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。
CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。当且仅当预期值 A 和内存值 V 相同时,将内存值 V 修改为 B,否则什么都不做。
JDK1.5 中引入了底层的支持,在 int、long 和对象的引用等类型上都公开了 CAS 的操作,并且 JVM 把它们编译为底层硬件提供的最有效的方法,在运行 CAS 的平台上,运行时把它们编译为相应的机器指令。在 java.util.concurrent.atomic 包下面的所有的原子变量类型中,比如 AtomicInteger,都使用了这些底层的JVM支持为数字类型的引用类型提供一种高效的 CAS 操作。
在 CAS 操作中,会出现ABA 问题
。CAS算法实现一个重要前提:需要取出内存中某时刻的数据,而在下一时刻比较并替换。但是在这个时间差内会导致数据的变化。比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且two进行了一些操作变成了B,然后two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后one操作成功。尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。如果链表的头在变化了两次后恢复了原值,但是不代表链表就没有变化。
有个简单的方案解决ABA问题:不要单纯的更新某个引用的值,例如更新两个值,包括一个引用和一个版本号,即使这个值由 A 变为 B,然后 B 变为 A,版本号也是不同的,由此可以判断这个值是改变了的。
AtomicStampedReference 和 AtomicMarkableReference 支持在两个变量上执行原子的条件更新。AtomicStampedReference 更新一个 “对象-引用” 二元组,通过在引用上加上 “版本号”,从而避免 ABA 问题,AtomicMarkableReference 将更新一个“对象引用-布尔值”的二元组。