一、CAS是什么?
CAS是Compare And Swap(比较并替换)的缩写。属于硬件同步原语,处理器提供了基本内存操作的原子性保证。CSA操作需要输入两个数值,一个旧值A(期望操作前的值)和一个新值B,在操作期间先对旧值进行比较,若没有发生变化,才交换新值,发生变化则不交换。
二、CAS的原理
1.CAS实现过程
线程会先去比较内存中的值与旧值是否相等,相等则将新值替换原来的旧值,否则自旋。(内存条在硬件方面保证了同一时刻只能有一个线程修改)实际java是通过JVM去调用操作系统,JVM是通过unsafe调用操作系统实现cas操作
2.Unsafe实现CAS操作
代码示例:
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class UnsafeDemo {
volatile int i = 0;
private static Unsafe unsafe;
private static long valueOffset;
static {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
Field iField = CounterUnsafe.class.getDeclaredField("i");
valueOffset = unsafe.objectFieldOffset(iField);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
public void add() {
for (;;){
int current = unsafe.getIntVolatile(this, valueOffset);
if (unsafe.compareAndSwapInt(this, valueOffset, current, current+1))
break;
}
}
public static void main(String[] args) throws InterruptedException {
final UnsafeDemo ct = new UnsafeDemo();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 10000; j++) {
ct.add();
}
System.out.println("done...");
}
}).start();
}
Thread.sleep(6000L);
System.out.println(ct.i);
}
}
AtomicInteger也是通过偏移量来实现CAS操作的(java.util.concurrent.atomic包下面的类基本都是使用的Unsafe来实现CAS)
使用例子1:通过AtomicIntegerFieldUpdater实现对User属性的原子更新。 使用场景:已经存在的类,没有使用AtomicInteger来修饰属性(id,age),需要修改属性,就需要用 AtomicIntegerFieldUpdater来实现对属性进行CAS更新。注意:属性必须用volatile修饰,否则AtomicIntegerFieldUpdater没办法使用 输出结果: 使用例子2:这会出现原子性问题(多个线程获取都为null,然后都对owner修改),AtomicReference实现引用类型的更新,修改对象的引用。 使用后:
三、CAS的ABA问题
1、ABA问题解释:线程1、线程2同时读取i=0;线程1、线程2都要执行CAS操作,如果线程2操作完成在线程1之后,那么线程1执行完成之后,线程1再执行CAS(0,1),将i的值改为0;
最后的结果是线程1、线程2都执行成功(如果线程1执行完之后没有执行CAS(0,1),那么线程2执行CAS(0,1)会失败)。 问题原因:后面的1已经不是原来的1了 示例2:
解决ABA问题方法:加入版本号 存在ABA问题的代码示范:
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
public class Stack {
AtomicReference<Node> top = new AtomicReference<Node>();
public void push(Node node) {
Node oldTop;
do {
oldTop = top.get();
node.next = oldTop;
}
while (!top.compareAndSet(oldTop, node));
}
public Node pop(int time) {
Node newTop;
Node oldTop;
do {
oldTop = top.get();
if (oldTop == null) {
return null;
}
newTop = oldTop.next;
if (time != 0) {
LockSupport.parkNanos(1000 * 1000 * time);
}
}
while (!top.compareAndSet(oldTop, newTop));
return oldTop;
}
}
使用添加版本号的代码示例:
import java.util.concurrent.atomic.AtomicStampedReference;
import java.util.concurrent.locks.LockSupport;
public class ConcurrentStack {
AtomicStampedReference<Node> top =
new AtomicStampedReference<>(null, 0);
public void push(Node node) {
Node oldTop;
int v;
do {
v = top.getStamp();
oldTop = top.getReference();
node.next = oldTop;
}
while (!top.compareAndSet(oldTop, node, v, v+1));
}
public Node pop(int time) {
Node newTop;
Node oldTop;
int v;
do {
v = top.getStamp();
oldTop = top.getReference();
if (oldTop == null) {
return null;
}
newTop = oldTop.next;
if (time != 0) {
LockSupport.parkNanos(1000 * 1000 * time);
}
}
while (!top.compareAndSet(oldTop, newTop, v, v+1));
return oldTop;
}
}
AtomicStampedReference此类带有版本号,替换原有的 AtomicReference,这样解决了ABA问题
|