六、多线程-CAS

CAS

锁的开销极大。在某些场景,如保证一个变量的 read-modify-write操作的原子性。这种场景可以通过使用CAS解决而不需要用到锁。

CAS,Compare and swap 比较并交换,是一种乐观锁的实现方式。是一个中由处理器保证原子性的if-then-act操作。它通过提供一个变量内存位置,预期值(旧值)和新值。将预期值和变量的当前值进行比较,如果相等即证明变量并没有被改变,将该变量修改成新值。如果不相等则进行重试(预期值会重新加载),直到成功。

Unsafe类中通过CAS修改int类型变量源码。

/**
  *var1  AtomicInteger对象
  *var2  内存偏移量
  *var4  增加的值
  *var5  获取的变量原值,保存在var5用于当预期值
  *compareAndSwapInt会比较内存值和var5相等的话就会改变内存值(即AtomicIntege * r对象中的变量)。
  */
  public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            //获取内存的当前值
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
  }

原子变量

以上代码是原子遍历AtomicInteger自增代码的实现片段。JDK基于CAS提供了保证共享变量read-modify-write操作原子性的类。

分组类名
基本类型AtomicInteger,AtomicLong,AtomicBoolean
数组类型AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
字段更新AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicReferenceFieldUpdater
引用型AtomicReference,AtomicStampedReference,AtomicMarkableReference

AtomicInteger

方法作用
int accumulateAndGet(int x, IntBinaryOperator accumulatorFunction)使用将给定函数应用于当前值和给定值的结果原子更新当前值,返回更新后的值。
int addAndGet(int delta)将给定的值原子地添加到当前值。
boolean compareAndSet(int expect, int update)如果当前值 ==为预期值,则将该值原子设置为给定的更新值。
int decrementAndGet()原子减1当前值。
double doubleValue()返回此值 AtomicInteger为 double一个宽元转换后。
float floatValue()返回此值 AtomicInteger为 float一个宽元转换后。
int get()获取当前值。
int getAndAccumulate(int x, IntBinaryOperator accumulatorFunction)使用给定函数应用给当前值和给定值的结果原子更新当前值,返回上一个值。
int getAndAdd(int delta)将给定的值原子地添加到当前值。
int getAndDecrement()原子减1当前值。
int getAndIncrement()原子上增加一个当前值。
int getAndSet(int newValue)将原子设置为给定值并返回旧值。
int getAndUpdate(IntUnaryOperator updateFunction)用应用给定函数的结果原子更新当前值,返回上一个值。
int incrementAndGet()原子上增加一个当前值。
int intValue()将 AtomicInteger的值作为 int 。
void lazySet(int newValue)最终设定为给定值。
long longValue()返回此值 AtomicInteger为 long一个宽元转换后。
void set(int newValue)设置为给定值。
String toString()返回当前值的String表示形式。
int updateAndGet(IntUnaryOperator updateFunction)使用给定函数的结果原子更新当前值,返回更新的值。
boolean weakCompareAndSet(int expect, int update)如果当前值 ==为预期值,则将值设置为给定更新值。

#### ABA问题 以上说到,CAS是将预期值和内存当前值比较,通过比较结果来判断其他线程是否修改过该变量。但是如果存在其他线程修改变量后又改回原值(即预期值),在某些场景就会存在问题。

ABA问题例子

银行账户 500元(共享变量)

  1. 要取出50元,机器故障发送了2个请求A,B,此时两个请求的期望值都是500,新值450
  2. A请求执行完后,内存值变成450。所以第二个请求是不会成功的。
  3. 但是如果在B请求执行前,C又往账户存了50块。这时银行帐号变成500。B请求预期值满足提交成功,银行账户最终存款为 450。

这种情况下,存款少了50块。

ABA解决方案。

ABA问题可以通过版本号来解决,每次修改操作都添加一个版本号。例如刚才的取款操作加个版本号 1,在存款操作执行后版本号+1,变为2。取款的第二次请求执行时就会判断版本号不是1,执行失败。

ABA问题,原子变量AtomicStampedReference,AtomicMarkableReference用于解决ABA问题。

注意

  1. CAS只能保证一个共享变量的操作的原子性(原子性操作+原子性操作≠原子操作),如果要保持多个共享变量的操作的原子性,就必须使用锁。
  2. 如果变量更新多次失败,循环时间长开销大。
  3. ABA问题
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值