浅析CompareAndSet(CAS)底层源码
在 java.util.concurrent.atomic 这个包里面提供了一组原子类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由 JVM从等待队列中选择一个另一个线程进入。
包结构图

CAS就是Compare and Swap,比较并交换算法,处理同步问题的常见解决思路。需要有4个值:当前对象(this),内存地址(valueOffset),旧的预期值(expect),即将要更新的目标值(update)。CAS指令执行时,当且仅当内存地的值与预期值相等时,将内存地址的值修改为目标值,并返回true ,否则就什么都不做,返回false。整个比较并替换的操作是一个原子操作。
测试用例:
public class CASTest {
public static void main(String[] args) {
//设定初始值为10
AtomicInteger integer = new AtomicInteger(10);
/**
* 一个是期望值,一个是更新值,但期望值和原来的值相同时,才能够更改
*/
//设定期望值10,更新值15,并获取当前内存里的值
boolean flag=integer.compareAndSet(10, 15);
System.out.println(flag + "\t 当前值: " + integer.get());
//参照组1:设定期望值10,更新值20,并获取当前内存里的值
flag=integer.compareAndSet(10, 20);
System.out.println(flag+ "\t 当前值: " + integer.get());
//参照组2:设定期望值15,更新值20,并获取当前内存里的值
flag=integer.compareAndSet(15, 20);
System.out.println( flag+ "\t 当前值: " + integer.get());
}
}
----返回结果------
true 当前值: 15
false 当前值: 15
true 当前值: 20
compareAndSet方法的底层源码:
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
在多线程情况下容易造成"ABA问题"的产生,CAS 算法实现一个重要前提需要取出内存中某时刻的数据,而在下时刻比较并替换,那么在这个时间差类会导致数据的变化
ABA问题测试用例
public class ABADemo {
public static void main(String[] args) {
//设定初始值为10
AtomicInteger integer = new AtomicInteger(10);
new Thread(()->{
boolean flag=integer.compareAndSet(10, 15);
System.out.println(Thread.currentThread().getName()+"\t "+flag + "\t 当前值: " + integer.get());
flag=integer.compareAndSet(15, 20);
System.out.println(Thread.currentThread().getName()+"\t "+ flag+ "\t 当前值: " + integer.get());
},"thread-1").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean flag=integer.compareAndSet(20,10);
System.out.println(Thread.currentThread().getName()+"\t "+flag+ "\t 当前值: "+integer.get());
},"thread-2").start();
}
}
----返回结果------
thread-1 true 当前值: 15
thread-1 true 当前值: 20
thread-2 true 当前值: 10
解决ABA问题可以使用添加版本号来解决变更数据问题
解决ABA问题测试用例
public class ABADeal {
public static void main(String[] args) {
AtomicStampedReference<Integer> atomic = new AtomicStampedReference(10, 0);
new Thread(() -> {
int stamp = atomic.getStamp();
System.out.println(Thread.currentThread().getName() + "\t "+null+ "\t "+ " 第1次版本号: " + stamp+"\t 当前值: "+atomic.getReference());
boolean flag=atomic.compareAndSet(10, 15,
atomic.getStamp(), atomic.getStamp() + 1);
System.out.println(Thread.currentThread().getName() +"\t "+flag+"\t "+ " 第2次版本号: " + atomic.getStamp()+"\t 当前值: "+atomic.getReference());
flag=atomic.compareAndSet(15, 20,
atomic.getStamp(), atomic.getStamp() + 1);
System.out.println(Thread.currentThread().getName() +"\t "+flag+"\t "+ " 第3次版本号: " + atomic.getStamp()+"\t 当前值: "+atomic.getReference());
}, "thread-1").start();
new Thread(() -> {
int stamp = atomic.getStamp();
System.out.println(Thread.currentThread().getName() + "\t "+null+ "\t "+ " 第1次版本号: " + stamp+"\t 当前值: "+atomic.getReference());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean flag = atomic.compareAndSet(20, 10,
stamp, stamp + 1);
System.out.println(Thread.currentThread().getName() + "\t "+ flag+"\t "+" 第2次版本号: " + atomic.getStamp()+"\t 当前值: "+atomic.getReference());
}, "thread-2").start();
}
}
----返回结果------
thread-1 null 第1次版本号: 0 当前值: 10
thread-2 null 第1次版本号: 0 当前值: 10
thread-1 true 第2次版本号: 1 当前值: 15
thread-1 true 第3次版本号: 2 当前值: 20
thread-2 false 第2次版本号: 2 当前值: 20
600

被折叠的 条评论
为什么被折叠?



