引言
前面介绍的原子更新器都只是针对单个对象或者字段元素进行原子更新操作,在序章中提到的第四类复合型的原子更新器可以将一个引用变量与其他变量绑定到一起,实现复合的原子更新操作,这类更新器有两个: AtomicMarkableReference、AtomicStampedReference。
AtomicStampedReference
AtomicStampedReference是为了解决CAS操作过程中的ABA问题,它将一个引用类似和一个int型的标识版本号的变量作为二元组合整体,所以AtomicStampedReference也被称为带版本戳的原子引用类型。每一次试图原子更新引用类型的时候都至少需要比较其携带的版本号,如果版本号不一致将会更新失败,当然更新的时候对应的版本号也会一起被更新。还是以源码开始进行分析吧。
public class AtomicStampedReference<V> { //静态内部类Pair将对应的引用类型和版本号stamp作为它的成员 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); } } //作为一个整体的pair变量被volatile修饰 private volatile Pair<V> pair; //构造方法,参数分别是初始引用变量的值和初始版本号 public AtomicStampedReference(V initialRef, int initialStamp) { pair = Pair.of(initialRef, initialStamp); } .... private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); private static final long pairOffset = objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class); //获取pair成员的偏移地址 static long objectFieldOffset(sun.misc.Unsafe UNSAFE, String field, Class<?> klazz) { try { return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field)); } catch (NoSuchFieldException e) { NoSuchFieldError error = new NoSuchFieldError(field); error.initCause(e); throw error; } } }
由以上的源码可见, AtomicStampedReference内部有一个静态内部类Pair的实例变量pair来存放初始化AtomicStampedReference时传入的初始引用变量和初始版本号,注意它是被volatile修饰的,保证了其可见性;并且在每一次原子更新操作的时候都会生成新的pair实例替代旧的pair,即把真正的引用变量和版本号作为一个整体进行更新,所以在AtomicStampedReference内部还提前获取了该pair变量的偏移地址,为原子更新操作作准备。
接下来,看看它的方法。它的方法分为两类,一类是get方法,另一类就是原子更新方法:
//获取现在的引用类型变量的值 public V getReference() { return pair.reference; } //获取现在的版本号 public int getStamp() { return pair.stamp; } //同时获取版本号和引用变量,参数必须是一个长度的int数组, //因为int型的版本号不能通过引用传递,所以通过参数指定的数组进行返回。 //而对于的引用变量则直接返回。 public V get(int[] stampHolder) { Pair<V> pair = this.pair; stampHolder[0] = pair.stamp; return pair.reference; }
然后就是最重要的原子更新操作相关的方法:
//同时原子的更新对于的引用变量和版本号,当引用类型和版本号都和期望的值一致的时候。成功返回true //根据原子类序章,该方法只保证对自己所操作的变量的原子性和可见性,没有其他的happen-before规则成立。 //这里的源码虽然看起来是直接调用了compareAndSet,但是不保证运行时不被JVM替换掉真正的实现方法。 public boolean weakCompareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) { return compareAndSet(expectedReference, newReference, expectedStamp, newStamp); } //CAS原子更新操作,当引用类型和版本号都和预期的一致的时候进行原子更新,由于它是直接更新整个volatile修饰的pair变量, 所以满足可见性,有序性。成功返回true 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))); } //直接将原来的引用类型变量和版本号更改为新的值。 public void set(V newReference, int newStamp) { Pair<V> current = pair; if (newReference != current.reference || newStamp != current.stamp) this.pair = Pair.of(newReference, newStamp); } //当引用类型的值与期望的一致的时候,原子的更改版本号为新的值。该方法只修改版本号,不修改引用变量的值,成功返回true public boolean attemptStamp(V expectedReference, int newStamp) { Pair<V> current = pair; return expectedReference == current.reference && (newStamp == current.stamp || casPair(current, Pair.of(expectedReference, newStamp))); } //该方法是其他CAS方法的真正实现方法,其实就是通过unsafe的CAS方法将pair原子的更改为新的值。成功返回true private boolean casPair(Pair<V> cmp, Pair<V> val) { return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val); }
通过以上原子更新方法,可见 AtomicStampedReference就是利用了Unsafe的CAS方法+Volatile关键字对存储实际的引用变量和int的版本号的Pair实例进行更新。
AtomicMarkableReference
AtomicMarkableReference其实可以看作是 AtomicStampedReference的特例,因为AtomicMarkableReference是将一个引用变量和一个布尔变量作为一个二元组合整体,它的版本号相当于只有true和false,而不像AtomicStampedReference那样可以对每一次的更新都记录版本号,因为在有些场合我们只需要知道是否有人等我们关注的对象进行了更改,如果已经更改就不再作任何更改,例如对节点删除的标记,这是其实通过AtomicMarkableReference就能够达到我们的要求。
关于AtomicMarkableReference的源码,其实它和AtomicStampedReference的设计原理是一样的,只是将标识版本号的int型变量换成了布尔型的变量,还是以一个静态内部类Pair将这个引用变量和布尔变量组合在一起进行原子的更新。所以其源码就不再一一列举。
public class AtomicMarkableReferenceTest { public static void main(String[] args) { AtomicMarkableReference<String> amr = new AtomicMarkableReference<String>("ABC", false); boolean result = amr.attemptMark("ABC", true);//标记为true System.out.println(result); System.out.println(amr.isMarked());//返回是否被标记了,true AtomicStampedReference<String> asr = new AtomicStampedReference<String>("123", 0); boolean seted = asr.compareAndSet("123", "456", 0, 1);//原子的更新原值和版本号 System.out.println(seted); System.out.println(asr.getStamp());//获取新的版本号 1 } }
相关推荐
原子指令是特殊的硬件指令,以不可分的方式对一个或多个内存位置执行操作。无论其他处理器执行什么指令...但是,由于它们是低层的,并且只能对数据结构进行小的更新,因此使用它们来实现并行数据结构是一项艰巨的任务。
同步练习 1.1 原子结构.doc
本程序能够将你计算机的时钟与美国NIST原子时钟进行同步,显示本机时间及原子钟时间,显示时间误差,如果有时间误差,点击一下校准即可进行时间同步,非常方便。本程序在时间校准时需要连接到互联网,因为同步时间...
同步之原子操作.pptx
原子物理,核物理,理念原创 哲学指导科学思维 洞察宇宙之深层奥秘 必从最简单的理论开始 勇于开拓创新
原子结构同步练习.doc
此外讨论一个数据结构必须同时讨论在该类数据上执行的运算才有意义。 在许多类型的程序的设计中,数据结构的选择是一个基本的设计考虑因素。许多大型系统的构造经验表明,系统实现的困难程度和系统构造的质量都严重...
诸如d嵌段过渡金属的原子摩尔体积收缩或镧系金属的Eu和Yb原子体积异常之类的细节,除了原子的电子结构之外,还与核反应。 原子的原子核在恒星内部被合成,并且只有在迁移到恒星的外围之后才捕获电子,成为稳定的...
高中原子结构教案
高中化学人教版必修2同步练习 第1章 物质结构 元素周期律 第1节 第2课时 元素的性质和原子结构.doc
九年级化学上册同步教学课题2原子的结构共39张PPT学习教案.pptx
高一化学同步测试—原子结构[精选].doc
2018年秋九年级化学上册第1节构成物质的基本微粒3.1.3原子结构相对原子质量同步练习沪教版
原子结构与性质原子结构与元素的性质PPT课件.pptx
【高中新课标同步用书】高中物理 第十八章原子结构 2原子的核式结构模型(pdf)新人教版选修3-5
很好用的原子钟同步软件,服务器出现时钟同步异常可以使用该软件
MATLAB在原子结构学习中的应用.pdf
使用C语言实现数据结构 表,原子,链表,栈,集合,动态数组,环,序列,位向量,线程,异常,精度计算,内存管理,字符串 包含了不透明指针,断言处理时机,二级指针的用法,宏定义,复杂结构体,setjmp/longjmp,...
九年级化学上册同步教学课题原子的结构PPT学习教案.pptx
原子结构(flash)<P> <BR>