谈论高并发(十一)几个自旋锁的实现(五岁以下儿童)中使用了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. Jquery显示和隐藏元素或设为只读(含Ligerui的控件禁用,实例说明)

    一.隐藏和显示元素 $('#button_save_12').css('display', 'none'); // 隐藏按钮 $('#button_save_12').css('display', ' ...

  2. Bestcoder Round#45

    1001 给定数n,要我们求该数的二进制中有多少组1, 相邻的1称为1组, 直接位运算摸你即可 #include <stdio.h> #include <string.h> # ...

  3. Android定位功能

    不说废话,直接说说实现android定位有关的API吧. 这些API都在android.location包下,一共有三个接口和八个类.它们配合使用即可实现定位功能. 三个接口: GpsStatus.L ...

  4. STL 源代码分析 算法 stl_algo.h -- merge

    本文senlie原版的,转载请保留此地址:http://blog.csdn.net/zhengsenlie merge (应用于有序区间) ------------------------------ ...

  5. iOS Foundation 框架基类

    iOS Foundation 框架基类 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转 ...

  6. 【v2.x OGE课程 14】 控制使用

    在这里,精灵.动画精灵.button天才.经常使用的文本的使用 一个.相关精灵 1.加入精灵 //创建精灵 Sprite bar_up = new Sprite(400, 0, RegionRes.g ...

  7. org.apache.jasper.JasperException: An exception occurred processing JSP page /admin/jiaoshi/daochuEx

    org.apache.jasper.JasperException: An exception occurred processing JSP page /admin/jiaoshi/daochuEx ...

  8. mongodb 学习笔记05 --用户管理

    csdn的markdown编辑器真有够烂的,这篇文章又给弄丢了 启用认证 mongod 启动默认没有开启权限,你须要指定 –auth 启动.或者在配置文件里设置security.authorizati ...

  9. Spring3 MVC请求参数获取的几种场景

    访问/aaa/bbb所对应的@Controller @RequestMapping("/aaa")//类级别,可以不需要,如果要了,下面所有的请求路径前都需要加入/aaa publ ...

  10. 【Java收集的源代码分析】Hashtable源代码分析

    Hashtable简单介绍 Hashtable相同是基于哈希表实现的,相同每一个元素是一个key-value对,其内部也是通过单链表解决冲突问题,容量不足(超过了阀值)时.相同会自己主动增长. Has ...