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

线程1准备用CAS将变量的值由A替换为B,在此之前,线程2将变量的值由A替换为C,又由C替换为A,然后线程1执行CAS时发现变量的值仍然为A,所以CAS成功。

但实际上这时的现场已经和最初不同了,尽管CAS成功,但可能存在潜藏的问题,例如下面的例子:

以上就是由于ABA问题带来的隐患,各种乐观锁的实现中通常都会用版本戳version来对记录或对象标记,避免并发操作带来的问题。

在Java中,AtomicStampedReference<E>也实现了这个作用,它通过包装[E,Integer]的元组来对对象标记版本戳stamp,从而避免ABA问题。

例如下面的代码分别用AtomicInteger和AtomicStampedReference来对初始值为100的原子整型变量进行更新,AtomicInteger会成功执行CAS操作,而加上版本戳的AtomicStampedReference对于ABA问题会执行CAS失败:

package com.vatuse.thread;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference; public class ABATest { private static AtomicInteger atomicInt = new AtomicInteger(100); private static AtomicStampedReference<Integer> atomicStampedRef = new AtomicStampedReference<Integer>(100, 0); public static void main(String[] args) throws InterruptedException { Thread intT1 = new Thread(new Runnable() { @Override
public void run() {
atomicInt.compareAndSet(100, 101);
atomicInt.compareAndSet(101, 100);
} }); Thread intT2 = new Thread(new Runnable() { @Override public void run() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) { }
boolean c3 = atomicInt.compareAndSet(100, 101);
System.out.println(c3); // true
}
}); intT1.start();
intT2.start();
intT1.join();
intT2.join();
Thread refT1 = new Thread(new Runnable() { @Override
public void run(){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
atomicStampedRef.compareAndSet(100, 101, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
atomicStampedRef.compareAndSet(101, 100, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
} }); Thread refT2 = new Thread(new Runnable() { @Override
public void run() { int stamp = atomicStampedRef.getStamp(); try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
}
boolean c3 = atomicStampedRef.compareAndSet(100, 101, stamp, stamp + 1);
System.out.println(c3); // false
}
}); refT1.start();
refT2.start();
}
}

备注:AtomicStampedReference源代码里使用的是CopyOnWrite的策略来保证线程安全,更改前保持pair的应用,每次更改都会新生成一个pair。
AtomicMarkableReference也能达到类似的效果。

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. 51nod 1135 原根(原根)

    题意 题目链接 Sol 可以证明素数的原根不会超过他的\(\frac{1}{4}\) 那么预处理出\(P - 1\)的所有的质因数\(p_1, p_2 \dots p_k\),暴力判断一下,如果$\e ...

  2. canvas image array(canvas图片阵列)

    code:   <!DOCTYPE html> <html> <head>  <title>hehe</title>  </head& ...

  3. MySQL创建子视图并查看的时候,字符集报错问题

    进入命令行模式, 如果MySQL数据库已经安装好,可以使用下列SQL命令查看MySQL当前的字符集设置: mysql> show variables like 'character_set_%' ...

  4. RocketMQ读书笔记4——NameServer(MQ的协调者)

    [NameServer简述] 对于一个消息队列集群来说,系统由很多机器组成,每个机器的角色.IP地址都不相同,而且这些信息是变动的(如在某些情况下,会有新的Producer或Consumer加入). ...

  5. Android Studio设置代码风格

    进入settings,然后搜索CodeStyle选择Java进入如下界面 scheme选择project

  6. C#mail发送

    这里,简单封装一个函数来发送邮件,代码如下: /// <summary> /// 邮件发送辅助类 /// </summary> public class MailHelper ...

  7. windows线程时间打印

    https://blog.csdn.net/xingcen/article/details/70084029

  8. zend studio 破解、汉化和字体颜色及快捷键相关设置

    下载:http://www.geekso.com/component/zendstudio-downloads/ 破解:http://www.geekso.com/ZendStudio9-key/ 注 ...

  9. Android(java)学习笔记1:多线程的引入

    1. 多线程的引入:

  10. ORACLE查询删除重复记录

     比如现在有一人员表 (表名:peosons) 若想将姓名.身份证号.住址这三个字段完全相同的记录查询出来 复制代码 代码如下: select p1.*   from persons  p1,pers ...