AtomicStampedReference
类提供了对对象引用变量的原子读和写,
AtomicStampedReference
是指多个试图更改同一AtomicStampedReference的线程不会使AtomicStampedReference最终处于不一致的状态。
AtomicStampedReference
和AtomicReference 不一样的是,
AtomicStampedReference在内部同时保留对象引用和数据戳,引用和数据戳可以通过compareAndSet()方法使用单CAS操作进行交换.
AtomicStampedReference
设计是为了解决AtomicReference
不能解决的ABA问题,ABA问题后面会讲解。
创建AtomicStampedReference
创建 AtomicStampedReference
实例:
Object initialRef = null;
int initialStamp = 0;
AtomicStampedReference atomicStampedReference =
new AtomicStampedReference(intialRef, initialStamp);
创建有类型的AtomicStampedReference
可以通过泛型创建指定类型的AtomicStampedReference
,下面是代码:
String initialRef = null;
int initialStamp = 0;
AtomicStampedReference<String> atomicStampedReference =
new AtomicStampedReference<String>(
initialRef, initialStamp
);
创建了一个String类型的 AtomicStampedReference,如果知道引用的类型,一般建议使用AtomicStampedReference 时使用泛型。
获取AtomicStampedReference引用
可以通过AtomicStampedReference
的getReference方法获取存储在AtomicStampedReference中的引用。如果不使用泛型,getReference()将返回Object对象的引用,如果使用了泛型,将返回创建AtomicStampedReference
变量时时候使用的泛型,下面是首先是不使用泛型 AtomicStampedReference
的getReference()
例子:
String initialRef = "first text";
AtomicStampedReference atomicStampedReference = (String)
new AtomicStampedReference(initialRef, 0);
String reference = atomicStampedReference.getReference();
注意有必要将getReference()返回的引用强制转换为字符串,因为在AtomicStampedReference为非类型化时,getReference()将返回一个对象引用。
下面是使用泛型的AtomicStampedReference
例子:
String initialRef = "first text";
AtomicStampedReference<String> atomicStampedReference =
new AtomicStampedReference<String>(
initialRef, 0
);
String reference = atomicStampedReference.getReference();
注意,这儿不再需要使用强制类型转换,因为使用了泛型,在编译时候已经知道了返回的是String引用。
获取AtomicStampedReference的数据戳
AtomicStampedReference
同时还有一个getStamp()
方法,用来获取内部存储的数据戳,下面是代码:
String initialRef = "first text";
AtomicStampedReference<String> atomicStampedReference =
new AtomicStampedReference<>(initialRef, 0);
int stamp = atomicStampedReference.getStamp();
原子性获取引用和数据戳
AtomicStampedReference
可以通过一个简单的,原子操作get()方法同时获取到引用和数据戳,可以将int类型的数组作为参数传到get()方法,并返回引用类型,同时数据戳会存储在int类型数组的第一个元素,下面是代码:
String initialRef = "text";
String initialStamp = 0;
AtomicStampedReference<String> atomicStampedReference =
new AtomicStampedReference<>(
initialRef, initialStamp
);
int[] stampHolder = new int[1];
String ref = atomicStampedReference.get(stampHolder);
System.out.println("ref = " + ref);
System.out.println("stamp = " + stampHolder[0]);
设置AtomicStampedReference的引用
可以通过AtomicStampedReference
实例的set()
方法存储引用. 在没有使用 AtomicStampedReference
实例的set()方法中第一个参数是使用的
Object
对象引用,
在使用泛型的 AtomicStampedReference
的set()
方法第一个参数是定义时的泛型类型,下面是
AtomicStampedReference
的set()
代码:
AtomicStampedReference<String> atomicStampedReference =
new AtomicStampedReference<>(null, 0);
String newRef = "New object referenced";
int newStamp = 1;
atomicStampedReference.set(newRef, newStamp);
我们看到无论使不使用泛型的set()
方法都没有什么区别,唯一的不一样是编译的时候应该控制了类型
。
CAS操作AtomicStampedReference引用
AtomicStampedReference
类包含了一个有用的
compareAndSet()
方法, compareAndSet()方法将一个期望的引用与存储在
AtomicStampedReference
实例中的值比较,如果引用与数据戳都相等
(equals()
或者 ==
),那么 AtomicStampedReference
实例将被设置为新的引用,compareAndSet()
如果设置成功将返回ture否则返回false.
下面是AtomicStampedReference
的compareAndSet()
例子:
String initialRef = "initial value referenced";
int initialStamp = 0;
AtomicStampedReference<String> atomicStringReference =
new AtomicStampedReference<String>(
initialRef, initialStamp
);
String newRef = "new value referenced";
int newStamp = initialStamp + 1;
boolean exchanged = atomicStringReference
.compareAndSet(initialRef, newRef, initialStamp, newStamp);
System.out.println("exchanged: " + exchanged); //true
exchanged = atomicStringReference
.compareAndSet(initialRef, "new string", newStamp, newStamp + 1);
System.out.println("exchanged: " + exchanged); //false
exchanged = atomicStringReference
.compareAndSet(newRef, "new string", initialStamp, newStamp + 1);
System.out.println("exchanged: " + exchanged); //false
exchanged = atomicStringReference
.compareAndSet(newRef, "new string", newStamp, newStamp + 1);
System.out.println("exchanged: " + exchanged); //true
例子首先创建了 AtomicStampedReference
,然后通过compareAndSet()
设置引用和数据戳,第二次在调用 compareAndSet()
没有成功,因为第一次的
initialRef
与存储值一样,这时内部存储的是newRef
,所以在调用compareAndSet()失败。
第二段,首先initialStamp
与期望值newStamp
不一样更新失败,此时存储的为newStamp
,再次调用compareAndSet()
则成功。
参考: https://blog.csdn.net/cgsyck/article/details/108026037
http://tutorials.jenkov.com/java-util-concurrent/atomicstampedreference.html
http://tutorials.jenkov.com/java-concurrency/non-blocking-algorithms.html