谈论高并发(十二)分析java.util.concurrent.atomic.AtomicStampedReference看看如何解决源代码CAS的ABA问题
于谈论高并发(十一)几个自旋锁的实现(五岁以下儿童)中使用了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问题的更多相关文章
- Java并发—原子类,java.util.concurrent.atomic包(转载)
原子类 Java从JDK 1.5开始提供了java.util.concurrent.atomic包(以下简称Atomic包),这个包中 的原子操作类提供了一种用法简单.性能高效.线程安全地更新一个变量 ...
- 高并发编程基础(java.util.concurrent包常见类基础)
JDK5中添加了新的java.util.concurrent包,相对同步容器而言,并发容器通过一些机制改进了并发性能.因为同步容器将所有对容器状态的访问都串行化了,这样保证了线程的安全性,所以这种方法 ...
- 聊聊高并发(二十)解析java.util.concurrent各个组件(二) 12个原子变量相关类
这篇说说java.util.concurrent.atomic包里的类,总共12个.网上有非常多文章解析这几个类.这里挑些重点说说. watermark/2/text/aHR0cDovL2Jsb2cu ...
- 原子类java.util.concurrent.atomic.*原理分析
原子类java.util.concurrent.atomic.*原理分析 在并发编程下,原子操作类的应用可以说是无处不在的.为解决线程安全的读写提供了很大的便利. 原子类保证原子的两个关键的点就是:可 ...
- java并发编程:线程安全管理类--原子包--java.util.concurrent.atomic
java.util.concurrent.atomic 的描述 AtomicBoolean 可以用原子方式更新的 boolean 值. AtomicInteger 可以用原子方式更新的 int 值. ...
- 并发之java.util.concurrent.atomic原子操作类包
15.JDK1.8的Java.util.concurrent.atomic包小结 14.Java中Atomic包的原理和分析 13.java.util.concurrent.atomic原子操作类包 ...
- Java多线程:CAS与java.util.concurrent.atomic
锁的几种概念 悲观锁 总是假设最坏的情况,每次获取数据都认为别人会修改,所以拿数据时会上锁,一直到释放锁不允许其他线程修改数据.Java中如synchronized和reentrantLock就是这种 ...
- java.util.concurrent.atomic 类包详解
java.util.concurrent包分成了三个部分,分别是java.util.concurrent.java.util.concurrent.atomic和java.util.concurren ...
- java.util.concurrent.atomic 包详解
Atomic包的作用: 方便程序员在多线程环境下,无锁的进行原子操作 Atomic包核心: Atomic包里的类基本都是使用Unsafe实现的包装类,核心操作是CAS原子操作 关于CAS compar ...
随机推荐
- HDU 2828 DLX搜索
Lamp Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submi ...
- ExtJs自学教程(1):从一切API开始
称号 记得 本系列文章是不是引进全套焦点ExtJs使用,您只需专注于解决ExtJs思考问题.人们不写长篇大论.别人能学会自立.l 有些人只要学会CSS的javascript对于英文不至于很蹩脚(以辅 ...
- const使用摘要
const在四种方案如以下: int b = 500; const int *a = &b; ①(底层const) int const *a = &b; ②(底层const) int ...
- Ubuntu在下面LAMP(Linux+Apache+MySQL+PHP) 开发环境的搭建
LAMP在行业是一个非常流行的词.此4字母代表Linux.Apache,MySQL和PHP. LAMP其高效.灵活的特点已经成为中小企业的首选. 它已经推出了快速构建LAMP道路. 1 在Ubuntu ...
- hdu3037(lucas定理)
给定n,m,p 表示<=m个豆子放在n棵树上,一共有多少种方案数, 总的方案书mod p 如果将m个豆子放在n棵树上, 可以使用插板法 得到方案数是C(n+m-1,n-1) 那么将0< ...
- JavaEEB2C网上商城前端系统
问题的提出: 电子商务已经成为人们生活中不可或缺的组成部分,它提供了一种足不出户就可以挑选.购买.使用商品的方式.在众多点上网站中,综合类的B2C电商以其较高的可信度,丰富的商品类目,得到消费者的青睐 ...
- crm2011js子网格导航栏字段事件操作
- accept功能
accept()功能 系统调用 accept() 这将是一个有点陌生的地方! 你可以想象发生 这种事情:这是非常远离你通过倾听 (listen()) 的port连接 (connect()) 你的机器. ...
- Sandcastle生成帮助文档
http://www.cnblogs.com/net515/p/3311584.html Sandcastle帮助文档生成器使用介绍 一.软件介绍 Sandcastle是一个管理类库的文档 ...
- Ising模型(伊辛模型)
Ising模型(伊辛模型)是一个最简单且能够提供非常丰富的物理内容的模型.可用于描写叙述非常多物理现象,如:合金中的有序-无序转变.液氦到超流态的转变.液体的冻结与蒸发.玻璃物质的性质.森林火灾.城市 ...