文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

CAS与ABA问题及解决方式

2024-12-03 10:12

关注

 要了解ABA问题,我们得先知道什么是CAS,CAS 全称是 compare and swap,是一种用于在多线程环境下实现同步功能的机制。CAS的出现主要是为了解决多线程并发情况下,数据的不一致问题。

CAS底层原理

CAS 的思想很简单:三个参数,一个当前内存值 V、旧的预期值 A、即将更新的值 B,当且仅当预期值 A 和内存值 V 相同时,将内存值修改为 B 并返回 true,否则什么都不做,并返回 false

Unsafe类

Unsafe类是CAS的核心类,由于Java方法无法直接访问底层系统,需要通过本地(native)方法来访问,基于该类可以直接操作特定内存的数据。Unsafe类存在与sum.misc包中,其内部实现是C++写的,我从JDK1.8源码中截取了关键代码

  1. UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) 
  2.  UnsafeWrapper("Unsafe_CompareAndSwapInt"); 
  3.  oop p = JNIHandles::resolve(obj); 
  4.  jint* addr = (jint *) index_oop_from_field_offset_long(p, offset); 
  5.  return (jint)(Atomic::cmpxchg(x, addr, e)) == e; 
  6. UNSAFE_END 

从上面代码可以看出最后调用的是Atomic:comxchg这个方法,这个方法的实现放在hotspot下的os_cpu包中,说明这个方法的实现和操作系统、CPU都有关系,以多核CPU为例:

CAS问题

cas实现

从JDK1.5开始,java.util.concurrent包为我们提供了许多cas操作类诸如:AtomicInteger,

AtomicLong,AtomicReference,它提供了轻量级的锁机制有着更好的性能,但同时也会出现一些问题,我们通过一张图来说明:


上图运行过程中可能会出现两个问题:

ABA问题的优化

ABA问题导致的原因,是CAS过程中只简单进行了“值”的校验,再有些情况下,“值”相同不会引入错误的业务逻辑(例如库存),有些情况下,“值”虽然相同,却已经不是原来的数据了。那如何能避免ABA问题呢?优化的方式也很简单,就是不能只对值进行比较,通过对值打标签的方式就能很好的避免ABA问题。JAVA中也为我们提供了相应的处理类AtomicStampReferenceAtomicStampReference在cas的基础上增加了一个标记stamp,使用这个标记可以用来觉察数据是否发生变化,给数据带上了一种实效性的检验。它有以下几个参数:

  1. //参数代表的含义分别是 期望值,写入的新值,期望标记,新标记值 
  2. public boolean compareAndSet(V expected,V newReference,int expectedStamp,int newStamp); 
  3.  
  4. public V getRerference(); 
  5.  
  6. public int getStamp(); 
  7.  
  8. public void set(V newReference,int newStamp); 

我们通过一个示例来说明:

  1. public class Test { 
  2.  
  3. private static AtomicReference<Integer> atomicReference = new AtomicReference<Integer>(100); 
  4.  
  5. public static void main(String[] args) { 
  6. new Thread(() -> { 
  7. atomicReference.compareAndSet(100, 101); 
  8. atomicReference.compareAndSet(101, 100); 
  9. },"t1").start(); 
  10.  
  11. new Thread(() -> { 
  12. try { 
  13. TimeUnit.SECONDS.sleep(1); 
  14. } catch (InterruptedException e) { 
  15. e.printStackTrace(); 
  16. System.out.println(atomicReference.compareAndSet(100, 2021) + "\t修改后的值:" + atomicReference.get()); 
  17. },"t2").start(); 

可以看到,线程2修改成功。输出结果:

  1. true 修改后的值:2021 

要解决ABA问题,可以增加一个版本号,当内存位置V的值每次被修改后,版本号都加1AtomicStampedReference内部维护了对象值和版本号,在创建AtomicStampedReference对象时,需要传入初始值和初始版本号, 当AtomicStampedReference设置对象值时,对象值以及状态戳都必须满足期望值,写入才会成功

  1. public class Test { 
  2.  
  3.  
  4. private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<Integer>(100,1); 
  5.  
  6. public static void main(String[] args) { 
  7. new Thread(() -> { 
  8. System.out.println("t1拿到的初始版本号:" + atomicStampedReference.getStamp()); 
  9.  
  10. //睡眠1秒,是为了让t2线程也拿到同样的初始版本号 
  11. try { 
  12. TimeUnit.SECONDS.sleep(1); 
  13. } catch (InterruptedException e) { 
  14. e.printStackTrace(); 
  15. atomicStampedReference.compareAndSet(100, 101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1); 
  16. atomicStampedReference.compareAndSet(101, 100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1); 
  17. },"t1").start(); 
  18.  
  19. new Thread(() -> { 
  20. int stamp = atomicStampedReference.getStamp(); 
  21. System.out.println("t2拿到的初始版本号:" + stamp); 
  22.  
  23. //睡眠3秒,是为了让t1线程完成ABA操作 
  24. try { 
  25. TimeUnit.SECONDS.sleep(3); 
  26. } catch (InterruptedException e) { 
  27. e.printStackTrace(); 
  28. System.out.println("最新版本号:" + atomicStampedReference.getStamp()); 
  29. System.out.println(atomicStampedReference.compareAndSet(100, 2021,stamp,atomicStampedReference.getStamp() + 1) + "\t当前 值:" + atomicStampedReference.getReference()); 
  30. },"t2").start(); 

输出结果:

  1. t1拿到的初始版本号:1 
  2. t2拿到的初始版本号:1 
  3. 最新版本号:3 
  4. false当前 值:100 

 【编辑推荐】

  1. Windows10这功能已如同残废!教你如何彻底关闭它
  2. C++和C++程序员谁先完蛋?
  3. 2021年值得关注的人工智能趋势
  4. RAID磁盘阵列到底适不适合你?一文读懂
  5. Windows 10是绝唱!微软新系统开始换版本号了

 

来源:编码是个技术活内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯