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 方法 } ```