谈论高并发(十一)几个自旋锁的实现(五岁以下儿童)中使用了java.util.concurrent.atomic.AtomicStampedReference原子变量指向工作队列的队尾,为何使用AtomicStampedReference原子变量而不是使用AtomicReference是由于这个实现中等待队列的同一个节点具备不同的状态,而同一个节点会多次进出工作队列,这就有可能出现出现ABA问题。

熟悉并发编程的同学应该知道CAS操作存在ABA问题。我们先看下CAS操作。

CAS(Compare and Swap) 比較并交换操作是一个三元操作: 目标地址的值T(arget)。期望值E(xpected),实际值R(eal),

1. 仅仅有当目标值T == 期望值E时。才会把目标值T设置为实际值R,否则不改变目标值

2. 无论目标值是否改变,都返回之前的目标值T

类似例如以下的逻辑:

package com.zc.lock;

public class CAS {
private int value; public synchronized int get(){
return value;
} public synchronized int compareAndSwap(int expected, int real){
int oldValue = value;
if(value == expected){
value = real;
}
return oldValue;
} public synchronized boolean compareAndSet(int expected, int real){
return (expected == compareAndSwap(expected, real));
}
}

CAS仅仅比較期望值和目标值是否相当。相当就设置新值。那么ABA问题就来了:

1. 因为CAS仅仅是值比較,比方目标是A, 期望值也是A, 那么CAS操作会成功。可是这时候目标A可能不是原来的那个A了。它可能是A变成了B,再变成了A。

所以叫ABA问题,非常形象。

ABA问题可能会使程序出错。比方限时有界队列锁中的节点有几个状态。尽管引用值是A。可是可能对象的状态已经变了,这时候的A实际已经不是原来的A了

2. 须要注意的是ABA问题不是说CAS操作的过程中A变成了ABA,CAS操作是原子操作,不会被打断。ABA问题场景例如以下:

先获取了A的值。然后再CAS(A, R), 这时候CAS中的A实际指向的对象的状态可能和它刚获得的时候的状态已经发送了改变。

</pre><pre name="code" class="java">A a = ref.get();
// 依据a的状态做一些操作
// do something
// CAS,这时候会出现ABA问题,a指向的对象可能已经变了
ref.compareAndSet(a, b)

解决ABA问题方法就是给状态设置时间戳,这是并发中加乐观锁的常见做法。假设状态的时间戳发生了改变,证明已经不是原来的对象了,所以操作失败

// 用int做时间戳
AtomicStampedReference<QNode> tail = new AtomicStampedReference<CompositeLock.QNode>(null, 0);
int[] currentStamp = new int[1];
// currentStamp中返回了时间戳信息
QNode tailNode = tail.get(currentStamp);
tail.compareAndSet(tailNode, null, currentStamp[0], currentStamp[0] + 1)

以下我们来看一下java.util.concurrent.atomic.AtomicStampedReference的源码是怎样实现的。

以下代码来自JDK1.7,条理非常清晰,实现有几个要点:

1. 创建一个Pair类来记录对象引用和时间戳信息,採用int作为时间戳,实际使用的时候时间戳信息要做成自增的,否则时间戳假设反复,还会出现ABA的问题。这个Pair对象是不可变对象,全部的属性都是final的。 of方法每次返回一个新的不可变对象

2. 使用一个volatile类型的引用指向当前的Pair对象。一旦volatile引用发生变化。变化对全部线程可见

3. set方法时,当要设置的对象和当前Pair对象不一样时。新建一个不可变的Pair对象

4. compareAndSet方法中,仅仅有期望对象的引用和版本号号和目标对象的引用和版本号好都一样时,才会新建一个Pair对象,然后用新建的Pair对象和原理的Pair对象做CAS操作

5. 实际的CAS操作比較的是当前的pair对象和新建的pair对象,pair对象封装了引用和时间戳信息

     private static class Pair<T> {
final T reference;
final int stamp;
private Pair(T reference, int stamp) {
this.reference = reference;
this.stamp = stamp;
}
static <T> Pair<T> of(T reference, int stamp) {
return new Pair<T>(reference, stamp);
}
} private volatile Pair<V> pair; public AtomicStampedReference(V initialRef, int initialStamp) {
pair = Pair.of(initialRef, initialStamp);
} public void set(V newReference, int newStamp) {
        Pair<V> current = pair;
        if (newReference != current.reference || newStamp != current.stamp)
            this.pair = Pair.of(newReference, newStamp);
    } public boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp) {
        Pair<V> current = pair;
        return
            expectedReference == current.reference &&
            expectedStamp == current.stamp &&
            ((newReference == current.reference &&
              newStamp == current.stamp) ||
             casPair(current, Pair.of(newReference, newStamp)));
    } private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
    private static final long pairOffset =
        objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class);     private boolean casPair(Pair<V> cmp, Pair<V> val) {
        return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
    }

版权声明:本文博客原创文章。博客,未经同意,不得转载。

谈论高并发(十二)分析java.util.concurrent.atomic.AtomicStampedReference看看如何解决源代码CAS的ABA问题的更多相关文章

  1. Java并发—原子类,java.util.concurrent.atomic包(转载)

    原子类 Java从JDK 1.5开始提供了java.util.concurrent.atomic包(以下简称Atomic包),这个包中 的原子操作类提供了一种用法简单.性能高效.线程安全地更新一个变量 ...

  2. 高并发编程基础(java.util.concurrent包常见类基础)

    JDK5中添加了新的java.util.concurrent包,相对同步容器而言,并发容器通过一些机制改进了并发性能.因为同步容器将所有对容器状态的访问都串行化了,这样保证了线程的安全性,所以这种方法 ...

  3. 聊聊高并发(二十)解析java.util.concurrent各个组件(二) 12个原子变量相关类

    这篇说说java.util.concurrent.atomic包里的类,总共12个.网上有非常多文章解析这几个类.这里挑些重点说说. watermark/2/text/aHR0cDovL2Jsb2cu ...

  4. 原子类java.util.concurrent.atomic.*原理分析

    原子类java.util.concurrent.atomic.*原理分析 在并发编程下,原子操作类的应用可以说是无处不在的.为解决线程安全的读写提供了很大的便利. 原子类保证原子的两个关键的点就是:可 ...

  5. java并发编程:线程安全管理类--原子包--java.util.concurrent.atomic

    java.util.concurrent.atomic 的描述 AtomicBoolean 可以用原子方式更新的 boolean 值. AtomicInteger 可以用原子方式更新的 int 值. ...

  6. 并发之java.util.concurrent.atomic原子操作类包

    15.JDK1.8的Java.util.concurrent.atomic包小结 14.Java中Atomic包的原理和分析 13.java.util.concurrent.atomic原子操作类包 ...

  7. Java多线程:CAS与java.util.concurrent.atomic

    锁的几种概念 悲观锁 总是假设最坏的情况,每次获取数据都认为别人会修改,所以拿数据时会上锁,一直到释放锁不允许其他线程修改数据.Java中如synchronized和reentrantLock就是这种 ...

  8. java.util.concurrent.atomic 类包详解

    java.util.concurrent包分成了三个部分,分别是java.util.concurrent.java.util.concurrent.atomic和java.util.concurren ...

  9. java.util.concurrent.atomic 包详解

    Atomic包的作用: 方便程序员在多线程环境下,无锁的进行原子操作 Atomic包核心: Atomic包里的类基本都是使用Unsafe实现的包装类,核心操作是CAS原子操作 关于CAS compar ...

随机推荐

  1. 关于QTP

    关于QTP,我也才接触,只是上来记录一下安装过程,免得以后忘记,又要查一堆资料. 简单介绍一下QTP: QTP的基本功能包括2个部分:一部分是提供给初级用户使用的关键字视图:另一部分是提供给熟悉VBS ...

  2. Python入门(转)

    Python 简洁的语法和对动态输入的支持,再加上解释性语言的本质, 使得它在大多数平台上的许多领域都是一个理想的脚本语言,特别适用于快速的应用程序开发 特点:简单.易学.免费.开源.高层语言.可移植 ...

  3. EJB通过ANT提高EJB应用程序的开发效率、无状态发展本地接口bean、开发状态bean

    该jboss集成到eclipse 关掉Jboss控制台新闻Ctrl+c,在MyEclipse→Servers→Jboss可配置JBoss. 通过ANT提高EJB应用的开发效率 在HelloWorld ...

  4. 关于扩展IP地址空间的几个方案的探讨

    摘  要:在IP地址紧缺的背景下,IPv6.NAPT.SuIP几种解决方案应运而生.分析.比较几种方案后,可知SuIP是最佳方案. 关键词:IPv6:NAPT:SuIP:IP地址空间扩展 The In ...

  5. 小猪的Android入门之路 Day 4 - part 1

    小猪的Android入门之路 Day 4 - part 1 Android事件处理机制之--基于监听的事件处理机制 本节引言: 在開始本个章节前,我们先回想下,如今我们已经知道了android的一些相 ...

  6. java Socket的怪异之处

    怪异之一: connect(SocketAddress endpoint):这个方法,尝试连接server端,如果连接不上,就抛出IOException异常.如果连接成功了,就继续执行下一步的代码. ...

  7. lunix shell 基础经常使用整理

     1   ps  -ef    显示正在执行的进程,pid 等信息  UID PID PPID C STIME TTY TIME CMD root 1 0 0 03:45 ? 00:00:02 ini ...

  8. WPF3D学习,立方体的绘制

    原文:WPF3D学习,立方体的绘制 以此为一个好的开始吧!一直都太懒,坚持写文章是个不错的开始!碰巧最近在研究WPF3D这块的知识,也为了练练自己的写作水平,整理这篇文章.新手上路,多多关照! 本文先 ...

  9. 小米2S twrp 中文,支持双系统

    更新日志: 更新日志: 汉化了要使用的功能 修改语言选择方式,修改为下拉方式 TDB(TrueDualBoot) 功能完美实现 **adb**功能,完美实现,无需特别操作(比CWM强大) 修改双系统切 ...

  10. Xamarin.Android 入门实例(3)之呼叫电话号码

    1.Main.axml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns: ...