CAS
compareAndSet 比较并交换: 比较当前工作内存中的值 和主内存中的值,如果这个值是期望的,那么执行替换,否则一直 循环(自旋锁)。
有点:自带原子性
缺点: 循环会耗时、一次只能保证一个共享变量、会出现ABA问题
public class TestCAS {
@Test
public void test(){
AtomicInteger atomicInteger=new AtomicInteger(200);
atomicInteger.compareAndSet(200,2001);
atomicInteger.getAndIncrement(); // 加1
System.out.println(atomicInteger.get());
atomicInteger.compareAndSet(200,2002);
System.out.println(atomicInteger.get());
}
}
unsafe类
java通过这个unsafe类操作内存。自旋锁
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2); // 获取var1内存地址+var2偏移量的值
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));// var1+ var2 跟var5 比较,满足就替换var5 + var4
return var5;
}
compareAndSwapInt是c++ native方法
ABA问题
比如两个线程都拿到同一个变量A=1,但是线程二把A的值改为3,再改回1。这个时候线程A拿到的1 是替换过后的1。
//ABA概述
@Test
public void test(){
AtomicInteger atomicInteger=new AtomicInteger(200);
// B线程,捣乱 200--》2001--》200
atomicInteger.compareAndSet(200,2001);
atomicInteger.compareAndSet(2001,200);
System.out.println(atomicInteger.get());
// 正常的A线程拿到的200,不是之前的200了
atomicInteger.compareAndSet(200,2002);
System.out.println(atomicInteger.get());
}
可通过乐观锁 加个版本号
AtomicStampedReference
@Test
public void testABA() throws InterruptedException {
AtomicStampedReference<Integer> atomicStampedReference=new AtomicStampedReference<>(10,1);
// a线程负责修改值 10--》11--》10
new Thread(()->{
int stamp = atomicStampedReference.getStamp();
System.out.println("a1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicStampedReference.compareAndSet(10, 11,
stamp, stamp + 1));
System.out.println("a2=>"+atomicStampedReference.getStamp());
System.out.println(atomicStampedReference.compareAndSet(11, 10,
atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
System.out.println("a2=>"+atomicStampedReference.getStamp());
},"a").start();
// b线程
new Thread(()->{
int stamp = atomicStampedReference.getStamp();
System.out.println("b1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicStampedReference.compareAndSet(10, 66,
stamp, stamp + 1));
System.out.println("b2=>"+atomicStampedReference.getStamp());
},"b").start();
TimeUnit.SECONDS.sleep(3);
}