AtomicReference

AtomicMarkableReference

AtomicStampedReference

  1. class DecimalAccountSafeCas implements DecimalAccount {
  2. AtomicReference<BigDecimal> ref;
  3. public DecimalAccountSafeCas(BigDecimal balance) {
  4. ref = new AtomicReference<>(balance);
  5. }
  6. @Override
  7. public BigDecimal getBalance() {
  8. return ref.get();
  9. }
  10. @Override
  11. public void withdraw(BigDecimal amount) {
  12. while (true) {
  13. BigDecimal prev = ref.get();
  14. BigDecimal next = prev.subtract(amount);
  15. if (ref.compareAndSet(prev, next)) {
  16. break;
  17. }
  18. }
  19. }
  20. }

ABA 问题及解决:

A被修改称了B,又被修改称了A之后,一个线程使用cas并不知道是否进行了改变,因为修改前后内容一致

主线程仅能判断出共享变量的值与最初值 A 是否相同,不能感知到这种从 A 改为 B 又 改回 A 的情况,如果主线程
希望:
只要有其它线程【动过了】共享变量,那么自己的 cas 就算失败,这时,仅比较值是不够的,需要再加一个版本号

AtomicStampedReference

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

输出为

  1. 15:41:34.891 c.Test36 [main] - main start...
  2. 15:41:34.894 c.Test36 [main] - 版本 0
  3. 15:41:34.956 c.Test36 [t1] - change A->B true
  4. 15:41:34.956 c.Test36 [t1] - 更新版本为 1
  5. 15:41:35.457 c.Test36 [t2] - change B->A true
  6. 15:41:35.457 c.Test36 [t2] - 更新版本为 2
  7. 15:41:36.457 c.Test36 [main] - change A->C false

AtomicStampedReference 可以给原子引用加上版本号,追踪原子引用整个的变化过程,如: A -> B -> A ->
C ,通过AtomicStampedReference,我们可以知道,引用变量中途被更改了几次。

但是有时候,并不关心引用变量更改了几次,只是单纯的关心是否更改过,所以就有了

AtomicMarkableReference 返回值可以判断是否进行了修改而不是返沪修改次数