在运用CAS做Lock-Free操作中有一个经典的ABA问题:

线程1准备用CAS将变量的值由A替换为B,在此之前,线程2将变量的值由A替换为C,又由C替换为A,然后线程1执行CAS时发现变量的值仍然为A,所以CAS成功。但实际上这时的现场已经和最初不同了,尽管CAS成功,但可能存在潜藏的问题,例如下面的例子:

现有一个用单向链表实现的堆栈,栈顶为A,这时线程T1已经知道A.next为B,然后希望用CAS将栈顶替换为B:

head.compareAndSet(A,B);

在T1执行上面这条指令之前,线程T2介入,将A、B出栈,再pushD、C、A,此时堆栈结构如下图,而对象B此时处于游离状态:

此时轮到线程T1执行CAS操作,检测发现栈顶仍为A,所以CAS成功,栈顶变为B,但实际上B.next为null,所以此时的情况变为:

其中堆栈中只有B一个元素,C和D组成的链表不再存在于堆栈中,平白无故就把C、D丢掉了。

以上就是由于ABA问题带来的隐患,各种乐观锁的实现中通常都会用版本戳version来对记录或对象标记,避免并发操作带来的问题,在Java中,AtomicStampedReference<E>也实现了这个作用,它通过包装[E,Integer]的元组来对对象标记版本戳stamp,从而避免ABA问题,例如下面的代码分别用AtomicInteger和AtomicStampedReference来对初始值为100的原子整型变量进行更新,AtomicInteger会成功执行CAS操作,而加上版本戳的AtomicStampedReference对于ABA问题会执行CAS失败:

  1. import java.util.concurrent.TimeUnit;
  2. import java.util.concurrent.atomic.AtomicInteger;
  3. import java.util.concurrent.atomic.AtomicStampedReference;
  4.  
  5. public class AtomicStampedReferenceTest {
  6. private static AtomicInteger atomicInt = new AtomicInteger(100);
  7. private static AtomicStampedReference atomicStampedRef = new AtomicStampedReference(100, 0);
  8.  
  9. public static void main(String[] args) throws InterruptedException {
  10. Thread intT1 = new Thread(new Runnable() {
  11. @Override
  12. public void run() {
  13. atomicInt.compareAndSet(100, 101);
  14. atomicInt.compareAndSet(101, 100);
  15. }
  16. });
  17.  
  18. Thread intT2 = new Thread(new Runnable() {
  19. @Override
  20. public void run() {
  21. try {
  22. TimeUnit.SECONDS.sleep(1);
  23. } catch (InterruptedException e) {
  24. }
  25. boolean c3 = atomicInt.compareAndSet(100, 101);
  26. System.out.println(c3); // true
  27. }
  28. });
  29.  
  30. intT1.start();
  31. intT2.start();
  32. intT1.join();
  33. intT2.join();
  34.  
  35. Thread refT1 = new Thread(new Runnable() {
  36. @Override
  37. public void run() {
  38. try {
  39. TimeUnit.SECONDS.sleep(1);
  40. } catch (InterruptedException e) {
  41. }
  42. atomicStampedRef.compareAndSet(100, 101, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
  43. atomicStampedRef.compareAndSet(101, 100, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
  44. }
  45. });
  46.  
  47. Thread refT2 = new Thread(new Runnable() {
  48. @Override
  49. public void run() {
  50. int stamp = atomicStampedRef.getStamp();
  51. try {
  52. TimeUnit.SECONDS.sleep(2);
  53. } catch (InterruptedException e) {
  54. }
  55. boolean c3 = atomicStampedRef.compareAndSet(100, 101, stamp, stamp + 1);
  56. System.out.println(c3); // false
  57. }
  58. });
  59.  
  60. refT1.start();
  61. refT2.start();
  62. }
  63. }

用AtomicStampedReference解决ABA问题的更多相关文章

  1. AtomicStampedReference解决ABA问题

      在运用CAS做Lock-Free操作中有一个经典的ABA问题: 线程1准备用CAS将变量的值由A替换为B,在此之前,线程2将变量的值由A替换为C,又由C替换为A,然后线程1执行CAS时发现变量的值 ...

  2. CAS如何解决ABA问题

    点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 文章不定期同步公众号,还有各种一线大厂面试原题.我的学习系列笔记. CAS如何解决ABA问题 什么是ABA:在CAS过程中,线程1.线程2分 ...

  3. 并发中的Native方法,CAS操作与ABA问题

    Native方法,Unsafe与CAS操作 >>JNI和Native方法 Java中,通过JNI(Java Native Interface,java本地接口)来实现本地化,访问操作系统底 ...

  4. Java CAS 和ABA问题

    独占锁:是一种悲观锁,synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁. 乐观锁:每次不加锁,假设没有冲突去完成某项操作,如果因为冲突失败就重试,直到成功 ...

  5. 多线程同步工具——CAS原子变量

    这是我参考的一篇文章<基于CAS的乐观锁实现>,讲述的是一种需要CPU支持的执行技术CAS(Compare and Swap). 首先理解什么是原子性操作,意思是不能再拆分的操作,例如改写 ...

  6. JUC学习笔记--Atomic原子类

    J.U.C 框架学习顺序 http://blog.csdn.net/chen7253886/article/details/52769111 Atomic 原子操作类包 Atomic包 主要是在多线程 ...

  7. J.U.C CAS

    在JDK1.5之前,也就是J.U.C加入JDK之前,Java是依靠synchronized关键字(JVM底层提供)来维护协调对共享字段的访问,保证对这些变量的独占访问权,并且以后其他线程忽的该锁时,将 ...

  8. 并发-AtomicInteger源码分析—基于CAS的乐观锁实现

    AtomicInteger源码分析—基于CAS的乐观锁实现 参考: http://www.importnew.com/22078.html https://www.cnblogs.com/mantu/ ...

  9. ReentrantLock 如何实现非公平锁?和公平锁实现有什么区别

    reentrant 英[riːˈɛntrənt] 美[ˌriˈɛntrənt] 先学会读.单词原意是可重入的 考察显示锁的使用.可延伸知识点 独占锁 & 共享锁 独占锁 - 悲观锁(不能同时被 ...

随机推荐

  1. mysql 字符集

    mysql -u root -p 输入密码进入mysql show variables like 'character%'; --显示字符集,像这样 \q退出mysql, 更改mysql配置文件 vi ...

  2. python pip出错问题解决记录

    今天安装一下requests模块,遇到网络问题 pip install requests Retrying (Retry(total=4, connect=None, read=None, redir ...

  3. 解读socketserver源码

    解读python中SocketServer源码 再看继承 真正的大餐来之前,还是来点儿开胃菜!回顾一下关于类的继承的知识:    我们先看上面的代码,这是一个简单的类继承,我们可以看到父类Base和子 ...

  4. Python3学习之路~8.6 开发一个支持多用户在线的FTP程序-代码实现

    作业: 开发一个支持多用户在线的FTP程序 要求: 用户加密认证 允许同时多用户登录 每个用户有自己的家目录 ,且只能访问自己的家目录 对用户进行磁盘配额,每个用户的可用空间不同 允许用户在ftp s ...

  5. for循环的beak continue用法

    continue跳出该循环, for循环后面的都要执行.break直接中段循环 后面不执行了

  6. OI养老专题03:让坏人出列的约瑟夫问题

    问题是这样的:一共有2n个人,其中有n个好人,n个坏人.好人的编号是1~n,坏人的编号是n+1~2n.要求你求出最小的m(报数到m的人出局),让前n个出局的人都是坏人. 似乎除了暴力,我们想不出其它的 ...

  7. word_freq

    1) 博客开头给出自己的基本信息,格式建议如下: 学号:2017*****7193(保留前4位和后4位,中间用星号代替,避免泄露个人信息): 姓名:刘新飞,用你的真实姓名替代 我的码云仓库地址:[ht ...

  8. python-爬虫(3)---lxml匹配css

    百度首页  部分代码 <div class="s_tab_inner"> <b>网页</b> <a href="//www.ba ...

  9. java.lang.IllegalAccessError: org.apache.commons.dbcp.DelegatingPreparedStatement.isClosed()Z

    做spring和mybaits整合时出现的错误,让这个问题困扰了一早上,通过查资料终于把这个问题解决了 具体问题描述: java.lang.IllegalAccessError: org.apache ...

  10. 已有的PHP安装gd扩展

    第一步 安装依赖 1.安装xpm yum install libXpm-devel 2.安装zlib wget http://zlib.net/zlib-1.2.8.tar.gz tar -xzvf ...