CAS是Java中Unsafe类里面的方法,它的全称是CompareAndSwap,比较并交换的意思。主要功能是能够保证在多线程环境下,对于共享变量的修改的原子性。
CAS有四个操作数,分别是对象内存位置,对象中的变量的偏移量,变量预期值和新的值。
其机制当且仅当对象中的变量的偏移量的值为变量预期值时,则用新的值更新对象中遍历的偏移量的值。而这个过程是处理器提供的一个原子性命令,不会存在线程安全问题。
然而CAS是一个native方法,还是会先从内存地址中读取state的值,然后去比较,最后再修改,这样还是会存在原子性问题。
所以CAS的底层实现在多核CPU下,会增加一个Lock指令对缓存或者总线加锁,从而保证比较并替换着两个指令的原子性。也就是CAS实际上是利用处理器提供的CMPXCHG指令实现的,而处理器执行CMPXCHG指令是一个原子性操作。
CAS主要用在并发场景
第一个是J.U.C里面的Atomic的原子实现,比如AtomicInteger,AtomicLong。
第二个是实现多线程对共享资源竞争的互斥性质,比如在AQS,ConcurrentHashMap,ConcurrentLinkedQueue等。
CAS缺点
1.ABA问题
ABA的问题指的是在CAS更新的过程中,当读取到的值是A,然后准备赋值的时候仍然是A,但是实际上有可能A的值被改成了B,然后又被改回了A,这个CAS更新的漏洞就叫做ABA。只是ABA的问题大部分场景下都不影响并发的最终效果。
在JDK中的AtomicStampedReference类给每个变量的状态值都配备了一个时间戳,从而避免了ABA问题的产生。
2.高竞争下的开销问题
在并发冲突概率大的高竞争环境下,如果CAS一直失败,会一直重试,CPU开销较大。可以引入退出机制,如重试次数超过一定阈值后失败退出,当然更重要的是避免在高竞争环境下使用乐观锁。
3.功能限制
CAS只能保证单个变量操作的原子性,这意味着
(1)原子性不一定能保证线程安全,例如在Java中需要与volatile配合来保证线程安全。
(2)当涉及到多个变量时,CAS也无能为力。
除此之外,CAS的实现需要硬件层面处理器的支持,在Java中普通用户无法直接使用,只能借助atomic包下的元子类使用,灵活性受到限制。