CAS

Compare And Swap,针对一个变量,首先比较它的内存值与某个期望值是否相同,如果相同,就给它赋一个新值。
一个不可分割的原子操作,并且其原子性是直接在硬件层面得到保障的。
底层源码:
image.png

缺点:自旋 CAS 长时间地不成功,则会给 CPU 带来非常大的开销

使用CAS时会存在ABA 问题:当有多个线程对一个原子类进行操作的时候,某个线程在短时间内将原子类的值A修改为B,又马 上将其修改为A,此时其他线程不感知,还是会修改成功。

ABA会出问题的场景:
小明账户上有100元。现在小明取钱,小强汇钱,诈骗分子盗刷三个动作同时进行。
1,小明取50元。
2,诈骗分子盗刷50元。
3,小强给小明汇款50元。
ABA问题就是:
1,小明验证账户上有100元后,取出50元。——账上有50元。
2,小强去汇款,汇款不会验证小明账户的余额,直接加上50元。——账上有100元。
3,诈骗分子验证账户有100元后,取出50元。——账上有50元。
如果小强没有告诉小明自己汇钱,小明也没收到银行短信,那么小明就一直以为只有自己取款操作,最后损失了50元。

解决ABA问题方案:AtomicStampedReference
reference即我们实际存储的变量,stamp是版本,每次修改可以通过+1保证版本唯一性。这样 就可以保证每次修改后的版本也会往上递增。

Atomic原子操作类介绍

基本类型:AtomicInteger、AtomicLong、AtomicBoolean;
引用类型:AtomicReference、AtomicStampedRerence、AtomicMarkableReference;
数组类型:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
对象属性原子修改器:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
原子类型累加器(jdk1.8增加的类):DoubleAccumulator、DoubleAdder、 LongAccumulator、LongAdder、Striped64

AtomicInteger

AtomicInteger里的变量通过volatile保证可见性
image.png
原子性通过Unsafe类的compareAndSwapInt方法保证,
通过value变量在AtomicInteger对象的偏移量(valueOffset)可以获取内存地址,通过内存地址指向的内存值与expect值比较去判断update值是否更新

  1. public final boolean compareAndSet(int expect, int update) {
  2. return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
  3. }

但是CAS在竞争激烈的情况下会造成cpu空转,性能不高,jdk1.8出了LongAdder去提高性能。

LongAdder

LongAdder的基本思路就是分散热点去减少竞争,将value值分散到一个数组中,不 同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值进行CAS操作。
最后要获取真正的long值,只要将各个槽 中的变量值累加返回。sum方法线程不安全,要等线程都执行完成才能让获取到正确的值
image.png