ABA问题是什么

两个或多个线程操作了共享变量,主线程依然修改的是共享变量初始化值,引发的原子性被破坏的问题。
线程判断被修改对象是否可以正确写入的条件是对象的当前值和期望值是否一致。这个逻辑从一般意义上来说是正确的,但是可能出现一个小小的例外,就是当你获得当前数据后,在准备修改为新值,对象的值被其他线程连续修改了两次,而经过这2次修改后,对象的值又恢复为旧值,这样,当前线程就无法正确判断这个对象究竟是否被修改过,这就是所谓的ABA问题,可能会引发一些问题。

实例

  1. public class AtomicReferenceABATest {
  2. static AtomicReference<String> ref = new AtomicReference<>("A");
  3. public static void main(String[] args) throws InterruptedException {
  4. System.out.println("main start...");
  5. // 获取值 A
  6. String prev = ref.get();
  7. // 如果中间有其它线程干扰,发生了 ABA 现象
  8. other();
  9. Thread.sleep(1000);
  10. // 尝试改为 C
  11. System.out.println("change A->C {}" + ref.compareAndSet(prev, "C"));
  12. System.out.println(ref.get());
  13. }
  14. private static void other() throws InterruptedException {
  15. new Thread(() -> {
  16. System.out.println("change A->B {}" + ref.compareAndSet(ref.get(), "B"));
  17. System.out.println(ref.get());
  18. }, "t1").start();
  19. Thread.sleep(500);
  20. new Thread(() -> {
  21. System.out.println("change B->A {}" + ref.compareAndSet(ref.get(), "A"));
  22. System.out.println(ref.get());
  23. }, "t2").start();
  24. }
  25. }

main start... change A->B {}true B change B->A {}true A change A->C {}true C

解决方法

AtomicStampedReference是什么

加入了版本信息,每次会更新版本,从而避免还在原基础上修改。

  1. public class AtomicReferenceABASolveTest {
  2. static AtomicStampedReference<String> ref = new AtomicStampedReference<>("A" ,0);
  3. public static void main(String[] args) throws InterruptedException {
  4. System.out.println("main start...");
  5. // 获取值 A
  6. String prev = ref.getReference();
  7. // 获取版本号
  8. int stamp = ref.getStamp();
  9. System.out.println("版本 {}" + stamp);
  10. // 如果中间有其它线程干扰,发生了 ABA 现象
  11. other();
  12. Thread.sleep(1000);
  13. // 尝试改为 C
  14. System.out.println("change A->C {}" + ref.compareAndSet(prev, "C", stamp, stamp + 1));
  15. System.out.println(ref.getReference());
  16. }
  17. private static void other() throws InterruptedException {
  18. new Thread(() -> {
  19. System.out.println("change A->B {}" + ref.compareAndSet(ref.getReference(), "B", ref.getStamp(), ref.getStamp() + 1));
  20. System.out.println("更新版本为 {}" + ref.getStamp());
  21. System.out.println(ref.getReference());
  22. }, "t1").start();
  23. Thread.sleep(500);
  24. new Thread(() -> {
  25. System.out.println("change B->A {}" + ref.compareAndSet(ref.getReference(), "A", ref.getStamp(), ref.getStamp() + 1));
  26. System.out.println("更新版本为 {}" + ref.getStamp());
  27. System.out.println(ref.getReference());
  28. }, "t2").start();
  29. }
  30. }

main start... 版本 {}0 change A->B {}true 更新版本为 {}1 B change B->A {}true 更新版本为 {}2 A change A->C {}false A