1.cas是compareAndSwap:比较并交换
Unsafe类中有很多native方法使用到了此思想实现,比如
public final native boolean compareAndSwapInt(Object var1,
long var2,
int var4,
int var5);
第一个和第二个参数,用于取得内存中实际的值
第三个是期望值
第四个是想要修改成为的值
如果内存中实际取得的值和期望值不相等,修改失败,相等的话就修改成功
2.使用场景
volatile并不能保证原子性,java.util.concurrent.atomic包下的类可以配合volatile保证操作的原子性
java.util.concurrent.atomic下包中的各种类中的方法用到的就是cas,比如
AtomicInteger类中的各种方法,就是调用的Unsafe类中native的各种由cas思想实现的方法
测试cas配合原子类实现原子性:
public class AtomicIntegerDemo2 {
static volatile AtomicInteger atomicInteger=new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(()->{
for(int i=0;i<50000;i++){
atomicInteger.incrementAndGet();
}
},"t1");
Thread t2=new Thread(()->{
for(int i=0;i<50000;i++){
atomicInteger.incrementAndGet();
}
},"t2");
t1.start();
t2.start();
Thread.sleep(1);
System.out.println(atomicInteger.get());
}
}
原子类中的方法是调用的unsafe类的方法:
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
3.CAS的ABA问题
问题描述:
线程t2打算将共享变量的值由0改为1,就用cas的方法。线程t1在线程t2执行之前,将i的值由0更改为1又更改为0。虽然在t2的层面看来,没有区别,但在某些时候可能有问题。
问题解决:
使用AtomicStampedReference类中的compareAndSet方法,AtomicStampedReference类就相当于加另一个时间戳或者相当于加了一个版本号。
以下代码的意思就是线程t1将共享变量的值从0设置为1又设置为0,但每次更改都要将版本号/时间戳+1,线程t2再使用cas更改的时候,发现版本号/时间戳已经不再是0了,所以修改失败。
public class ABA {
static Integer i=1;
static AtomicStampedReference<Integer> atomicStampedReference=
new AtomicStampedReference<>(i,0);
public static void main(String[] args) {
new Thread(()->{
//先把0改为1,stamp由0改为1
atomicStampedReference.compareAndSet(0,1,0,1);
//再把1改为0,stamp由1改为2
atomicStampedReference.compareAndSet(1,0,1,2);
},"t1").start();
new Thread(()->{
try {
Thread.sleep(1);
boolean result=atomicStampedReference.compareAndSet(0,1,0,1);
if(result){
System.out.println("修改成功");
}else{
System.out.println("修改失败");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t2").start();
}
}