CAS算法

CAS算法

今天在看了《Java并发编程的艺术》,学习如何减少上下文切换的时候,里面说到了通过CAS算法来更新数据,而它不需要加锁。不太理解什么是CAS算法,所以在网上搜罗半天资料,看了很久才理解了,给大家整理了一下CAS算法。

1、概述

CAS的全程是:Compare And Swap(比较并交换),CAS是实现并发计算时常用到的技术,Java并发包中的很多类都使用了CAS技术,如ConcurrentHashMap,AtomicInteger原子操作等

CAS操作涉及到3个操作符:当前内存中的值、预估值、即将修改的新增,当且仅当预估值等于内存中的值的时候,才将新的值保存到内存中,否则什么都不做

2、作用

CAS可以将比较和交换转换为原子操作,这个原子操作直接由CPU保证,CAS可以保证共享变量赋值时的原子操作

3、特点

CAS是一种非阻塞算法的实现,它能在不使用锁的情况下实现多线程安全,所以CAS也是一种无锁算法

一个线程失败或挂起并不会导致其他线程也失败或挂起

4、案例

AtomicInteger的incrementAndGet()源码

  • AtomicInteger底层使用到了Unsafe类,它提供了原子操作,Unsafe类可以直接操作内存空间的值

  • Unsafe的CompareAndSwapInt()方法其实就是CAS方法,它会自旋判断当前内存中的值与预期的值是否相等,如果相等就会把内存中的值用更新值替换,如果不相等则不操作,以此来保证原子性

  • 基于硬件平台的汇编指令,在intel的CPU中,使用的是cmpxchg指令,也就是说CAS靠硬件实现,从而在硬件层面提升效率

5、优点

  1. 由于CAS是非阻塞的,可以避免优先级倒置和死锁等问题
  2. 性能好,使用无锁的方式,没有锁竞争带来的系统开销,也没有线程间频繁调度带来的开销

6、缺点

  1. 循环时间长开销大(自旋)
  • 如果CAS失败,会一直进行尝试,如果CAS长时间一直不成功,可能给CPU带来很大的开销
  • 解决方法:破坏调死循环,当超过一定时间或者一定次数时,return退出
  1. 只能保证一个共享变量的原子操作
  • 当对一个共享变量操作时,可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性
  • 解决方法:
    1. 用锁来保证原子性
    2. 把多个共享变量合成一个共享变量来操作
    3. 封装成对象 ;Java1.5开始,JDK提供了AtomicReference类来保证引用对象之前的原子性,可以把多个变量放到一个对象里来进行CAS操作
  1. ABA问题
  • 如果内存地址V初次读取的值是A,并且在准备赋值的时候检查到它的值仍然为A,那我们就能说明它的值没有被其他线程改变过吗?

    如果在这段期间它的值曾经被改称了B,然后又改为A,那CAS就会误认为它从来没有被修改过,这就是ABA问题

  • 解决方法

    1. Java并发包提供了一个带有标记的原子引用类AtomicStampedReference,它可以通过控制变量值的版本来保证CAS的正确性;即在变量前面添加版本号,每次变量更新的时候都把版本号+1,这样变化过程就变成A-B-A变成1A-2B-3A
    2. 增加时间戳
posted @ 2023-03-16 16:59  戒爱学Java  阅读(114)  评论(0)    收藏  举报