原子引用解决ABA问题
大约 2 分钟JavaJUC并发编程
ABA问题
狸猫换太子。

public class CasDemo {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2021);
/** 捣乱的线程 **/
System.out.println(atomicInteger.compareAndSet(2021, 2022));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2022, 2021));
System.out.println(atomicInteger.get());
/** 期望的线程 **/
System.out.println(atomicInteger.compareAndSet(2021, 2233));
System.out.println(atomicInteger.get());
}
}
结果:
true
2022
true
2021
true
2233
Process finished with exit code 0
对于我们平时写的SQL来说:乐观锁。
原子引用

public class CasDemo {
public static void main(String[] args) {
AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(2021, 1);
new Thread(() ->{
// 获得版本号
int stamp = atomicStampedReference.getStamp();
System.out.println("a1==>" + stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicStampedReference.compareAndSet(2021, 2022
, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
System.out.println("a2==>" + atomicStampedReference.getStamp());
System.out.println(atomicStampedReference.compareAndSet(2022, 2021
, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
System.out.println("a3==>" + atomicStampedReference.getStamp());
},"a").start();
new Thread(() ->{
// 获得版本号
int stamp = atomicStampedReference.getStamp();
System.out.println("b1==>" + stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicStampedReference.compareAndSet(2021, 2233
, stamp, stamp + 1));
System.out.println("b2==>" + atomicStampedReference.getStamp());
},"b").start();
}
}
结果:
a1==>1
b1==>1
false
false
a2==>1
false
a3==>1
b2==>1
Process finished with exit code
结果有问题。
- 看compareAndSet的源码,里面是使用 == 进行比较的。
- 由于new的时候声明泛型肯定是装箱类,这个时候传入值类型将会自动装箱。
- 自动装箱的后果就是地址不一致,使用==判断的结果就为false。
- 总结:最好不使用原子类型,使用原子类型得保证比较时候传入的为同一个装箱类。
Integer使用了对象缓存机制,默认范国是-128~127。推荐使用静态工厂方法valueof获取对象实例,而不是new。因为valueof使用缓存,而new一定会创建新的对象分配新的内存空间。

把数字改小一点。
public class CasDemo {
public static void main(String[] args) {
// AtomicStampedReference 注意:泛型是一个包装类,对象的引用问题
// 正常业务中,这里面都是一个个对象
AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);
new Thread(() ->{
// 获得版本号
int stamp = atomicStampedReference.getStamp();
System.out.println("a1==>" + stamp);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(1, 2
, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
System.out.println("a2==>" + atomicStampedReference.getStamp());
System.out.println(atomicStampedReference.compareAndSet(2, 1
, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
System.out.println("a3==>" + atomicStampedReference.getStamp());
},"a").start();
new Thread(() ->{
// 获得版本号
int stamp = atomicStampedReference.getStamp();
System.out.println("b1==>" + stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicStampedReference.compareAndSet(1, 6
, stamp, stamp + 1));
System.out.println("b2==>" + atomicStampedReference.getStamp());
},"b").start();
}
}
结果:
a1==>1
b1==>1
a2==>2
true
a3==>3
false
b2==>3
Process finished with exit code 0
原子引用和乐观锁的原理相同。