接下来,让我们给他说道说道。
进入正题:
1. 什么是 CAS?CAS(Compare-And-Swap)是比较并交换的意思,它是一条 CPU 并发原语,用于判断内存中某个值是否为预期值,如果是则更改为新的值,这个过程是原子的。
CAS机制当中使用了3个值:内存地址V,旧的预期值A,计算后要修改的新值B
- 两个线程同时对内存值V进行 *** 作,V初始值为1
- 线程1、线程2都对V加1计算,预期值A=1,新值B=2
- 线程2先提交,预期值A==V,更新成功,将V更新为2
- 线程1提交时4,发现预期值A=1,V=2,A!=V,提交失败,重新获取内存值V=2
- 线程1自旋,V=2,A=2,B=3,重新比较A==V成立,然后更新V=3,最后V=3结束
2. CAS 基本原理总结
更新一个变量的时候,只有当变量的预期值 A 和内存地址 V 中的实际值相同时,才会将内存地址 V 对应的值修改为 B,这个 *** 作就是CAS
CAS 主要包括两个 *** 作:Compare和Swap。
CAS 是一条 CPU 的原子指令,执行必须是连续的,执行过程中不允许被中断。
JDK 是在 1.5 版本后才引入 CAS *** 作,在sun.misc.Unsafe这个类中定义了 CAS 相关的方法
public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object x); public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x); public final native boolean compareAndSwapLong(Object o, long offset, long expected, long x);3. Java 中的应用
CAS主要封装在java并发编程工具包中,java.util.concurrent包
AtomicInteger 类解决 i++ 非原子性问题,通过volatile 关键字和 CAS *** 作来实现
查看下源码:
public final int getAndUpdate(IntUnaryOperator updateFunction) { int prev, next; do { prev = get(); next = updateFunction.applyAsInt(prev); } while (!compareAndSet(prev, next)); return prev; } public final int updateAndGet(IntUnaryOperator updateFunction) { int prev, next; do { prev = get(); next = updateFunction.applyAsInt(prev); } while (!compareAndSet(prev, next)); return next; } public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }4. 弊端
- 典型的ABA
ABA 是 CAS *** 作的一个经典问题,一个变量初始值为 A,修改为 B,然后又修改为 A,这个变量实际被修改过了,但是 CAS *** 作无法感知到。
解决这个问题也很简单,只要在变量前加版本号,每次变量更新了就把版本号加1
- 自旋开销
CAS 出现冲突后就会开始重复尝试,即自旋 *** 作,如果资源竞争非常激烈,自旋长时间不能成功就会给 CPU 带来非常大的开销。
优化:限制自旋的次数,避免过度消耗 CPU;
- 只能保证单个变量的原子性
当对一个共享变量执行 *** 作时,可以使用 CAS 来保证原子性,如果要对多个共享变量进行 *** 作时,CAS 是无法保证原子性的,比如需要将 i 和 j 同时加 1:
i++;j++;
优化:
1、使用 synchronized 进行加锁;
2、将多个变量 *** 作合成一个变量 *** 作。AtomicReference 类来保证引用对象之间的原子性,把多个变量放在一个对象里来进行CAS *** 作
AtomicReference 关键方法:
public final boolean compareAndSet(V expect, V update) { return unsafe.compareAndSwapObject(this, valueOffset, expect, update); }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)