CAS
CAS 全称 Compare And Swap,即内存中的值V,旧的值A,预期的值B,当且仅当内存值V的值等于旧的值A时才会将内存值V的值修改为B,否则放弃替换;
CAS 底层使用 lock cmpxchg 指令(X86架构) ● 原子整数 ● 原子引用 ● 原子数组 ● 字段更新器 ● 原子累加器 ● Unsafe
原理
通过调用 Unsafe 类 compareAndSwapXXX 方法实现,该方法为本地方法,方法有4个参数(对象、对象地址、预期的值、修改的值)
缺点
- ABA的问题
- 自旋时间过长;
- 只能保证一个共享原子操作;
ABA的问题
CAS操作可能带来ABA问题,因为CAS操作需要在操作值的时候,检查值有没有发生变化,如果没有发发生变化则更新。如果一个值原理是A,变成了B,又变成了A,那么使用CAS进行检查时会认为它的值没有变化,但是实际上却变了。
ABA问题的解决办法就是使用版本号,在变量前面追加版本号,每次变量更新时把版本号加1,那么A-B-A就会变成1A-2B-3A。
从jdk1.5开始,jdk中的Atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法的作用首先检查当前引用是否等于预期引用,并且检查当前标志是否等于预期标志,如果都相等,则以原子方式将该引用和标志的值设为给定的更新值。原子整数
- AtomicInteger
- AtomicBoolean
- AtomicLong
AtomicInteger
private volatile int value
使用 volatile 关键字修饰,默认初始值为 0;
| 方法 | 功能 |
|---|---|
| set(int newValue) | 设置新值 |
| get() | 获取当前的值 |
| getAndAdd(int delta) | 先获取值,再增加值 |
| addAndGet(int delta) | 先增加值,再获取值 |
| incrementAndGet() | 先自增1,再返回自增后的数据 |
| getAndIncrement() | 先返回当前的数据,再自增1 |
| decrementAndGet() | 先自减1,再返回自减后的数据 |
| getAndDecrement() | 先返回当前数据 |
| compareAndSet(int expect, int update) | 通过前值和更新值更新数据 |
| updateAndGet(IntUnaryOperator updateFunction) | 更新值,再获取 (updateFunction是一个入参的lambda表达式) |
| accumulateAndGet(int x, IntBinaryOperator accumulatorFunction) | x值和旧值做一个运算,这个运算是accumulatorFunction(accumulatorFunction是两个入参的lambda表达式) |
原子引用类型
- AtomicReference
: - AtomicStampedReference:通过加版本号解决ABA的问题;
AtomicMarkableReference
:通过值是否被改变解决ABA的问题; 原子数组
保证数组元素的线程安全
AtomicIntegerArray
- AtomicLongArray
-
字段更新器
利用字段更新器,可以针对对象的某个域(Field)进行原子操作,只能配合 Volatile 修饰的字段使用,否则出现参数异常错误;
AtomicReferenceFieldUpdater
: - AtomicIntegerFieldUpdater
: AtomicLongFieldUpdater
: // Student.class 表示对哪个类保证原子操作// String.class 表示对哪个属性类型保证原子操作// "name" 表示对哪个属性名称保证原子操作AtomicReferenceFieldUpdater<Student, String> updater =AtomicReferenceFieldUpdater.newUpdater(Student.class, String.class, "name");
原子累加器
LongAdder
- DoubleAdder
LongAdder
// 累加单元数组,懒惰初始化transient volatile Cell[] cells;// 基础值,如果没有竞争,则用 cas 累加这个域transient volatile long base;// 在 cells 创建或者扩容时,置为1,表示加锁transient volatile int cellsBusy;
Unsafe
// 获取 Unsafe 对象Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);Unsafe unsafe = (Unsafe) theUnsafe.get(null);
通过Unsafe对象执行CAS操作
```java package top.simba1949.app2;
import sun.misc.Unsafe; import java.lang.reflect.Field;
/**
- @author Anthony
@date 2020/10/28 15:45 */ public class Application {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);Unsafe unsafe = (Unsafe) theUnsafe.get(null);// 获取域的偏移量地址long idOffset = unsafe.objectFieldOffset(User.class.getDeclaredField("id"));long nameOffset = unsafe.objectFieldOffset(User.class.getDeclaredField("name"));User user = new User();// 执行 CAS 操作unsafe.compareAndSwapInt(user, idOffset, 0, 1);unsafe.compareAndSwapObject(user, nameOffset, null, "张三");System.out.println(user.toString());
} }
class User{ volatile int id; volatile String name; // 省略getter/setter 和 toString 方法 } ```
