当前位置:   article > 正文

什么叫做CAS

cas

原文链接:什么叫做CAS – 编程屋

1 什么叫做CAS?

CAS(compare and swap)比较并交换,在平时开发中其实很多底层都是用cas来实现的,像原子类的底层原理就是cas,乐观锁的底层原理也是cas。原子类的用法可见下面这篇博客

volatile ~原子类 – 编程屋

cas的特点:当多个线程同时使用cas去更新一个变量的时候,只有其中一个线程能够操作成功,其他的线程都能够操作失败,但是更新失败的线程不会阻塞,但失败的线程会自旋尝试更新(默认都有尝试更新的次数)。

CAS中的核心操作:内存值V、预期值A、要修改的值B。当且仅当预期值A和当前的内存值V相等时才会将内存值修改为B。

2 CAS带来了什么问题?

CAS带来的ABA问题

比如线程1已经将利用CAS修改变量值A,但是在修改之前其他线程已经将A变成了B,然后又变成了A,即A->B->A问题。因为线程1在操作的时候发现值依然为A,所以根据CAS的机制会操作成功,但是其实这个值已经被其他线程修改过了,只是线程1不知道而已,这就导致了ABA问题

demo实例:

  1. private static AtomicReference<Integer> atomicInteger = new AtomicReference(100);
  2. public static void main(String[] args) throws InterruptedException {
  3. System.out.println("=================以下是ABA问题产生==========================");
  4. new Thread(() -> {
  5. atomicInteger.compareAndSet(100, 101);
  6. atomicInteger.compareAndSet(101, 100);
  7. },"t1").start();
  8. new Thread(() -> {
  9. try {
  10. //暂停1s中t2线程,保证了上面的t1线程完成了一次ABA操作(当main
  11. // 线程启动之后,t1线程和t2线程不知道谁先执行,所以让t2线程先睡1秒,这样即使t2线程先执行的情况下。睡1s时,也会被t1线程抢到完成一次ABA的操作)
  12. TimeUnit.SECONDS.sleep(1);
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }
  16. boolean result = atomicInteger.compareAndSet(100, 2022);
  17. System.out.println("是否修改成功:" + result +"\t"+"修改之后的值为"+atomicInteger.get());
  18. },"t2").start();
  19. }

由demo可以看到两个线程,第一个线程将100改为101后,又将101改为了100,线程2依然可以将100改为其他的值(2022)。所以说CAS的确带来了ABA问题。

 3 如何解决ABA问题?

在java中,AtomicStampedReference通过包装[E,Integer]的元组来对象对象标记版本戳stamp,从而避免ABA问题。

demo示例:

  1. private static AtomicReference<Integer> atomicInteger = new AtomicReference(100);
  2. private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<Integer>(100,1);
  3. public static void main(String[] args) throws InterruptedException {
  4. System.out.println("=================以下是ABA问题产生==========================");
  5. new Thread(() -> {
  6. atomicInteger.compareAndSet(100, 101);
  7. atomicInteger.compareAndSet(101, 100);
  8. },"t1").start();
  9. new Thread(() -> {
  10. try {
  11. //暂停1s中t2线程,保证了上面的t1线程完成了一次ABA操作(当main
  12. // 线程启动之后,t1线程和t2线程不知道谁先执行,所以让t2线程先睡1秒,这样即使t2线程先执行的情况下。睡1s时,也会被t1线程抢到完成一次ABA的操作)
  13. TimeUnit.SECONDS.sleep(1);
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. boolean result = atomicInteger.compareAndSet(100, 101);
  18. System.out.println("是否修改成功:" + result +"\t"+"修改之后的值为"+atomicInteger.get());
  19. },"t2").start();
  20. try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) {}
  21. System.out.println("=================以下是ABA问题的解决==========================");
  22. new Thread(() -> {
  23. //拿到此时版本号
  24. int stamp = atomicStampedReference.getStamp();
  25. System.out.println(Thread.currentThread().getName()+"\t第1次版本号为"+stamp);
  26. //暂停1s钟t3线程,让cpu去调度t4线程拿到同一个初始版本号
  27. try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {}
  28. //100设置成101并增加版本号
  29. atomicStampedReference.compareAndSet(100, 101, atomicStampedReference.getStamp(),
  30. atomicStampedReference.getStamp() + 1);
  31. System.out.println(Thread.currentThread().getName()+"\t第2次版本号为"+atomicStampedReference.getStamp());
  32. //101设置成100并增加版本号
  33. atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(),
  34. atomicStampedReference.getStamp() + 1);
  35. System.out.println(Thread.currentThread().getName()+"\t第3次版本号为"+atomicStampedReference.getStamp());
  36. },"t3线程").start();
  37. new Thread(() -> {
  38. //拿到此时版本号
  39. int stamp = atomicStampedReference.getStamp();
  40. System.out.println(Thread.currentThread().getName()+"\t第1次版本号为"+stamp);
  41. //暂停t4线程3秒钟,保证t3线程完成一次ABA操作
  42. try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { }
  43. //100设置成101并增加版本号
  44. boolean refResult = atomicStampedReference.compareAndSet(100, 2021, stamp, stamp + 1);
  45. System.out.println("是否修改成功:" + refResult +"\t"+"实际最新值为"+atomicStampedReference.getReference()+"\t"+"当前实际版本号为:"+atomicStampedReference.getStamp());
  46. },"t4线程").start();

控制台输出:

  可以看出,加入了版本号控制,解决了ABA的问题

4 CAS有什么缺点?

1)ABA问题

2)循环时间长,开销较大

以上只是部分内容,为了维护方便,本文已迁移到新地址:什么叫做CAS – 编程屋

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/293835
推荐阅读
相关标签
  

闽ICP备14008679号