程序员求职经验分享与学习资料整理平台

网站首页 > 文章精选 正文

java并发-AtomicStampedReference

balukai 2025-05-10 20:00:10 文章精选 4 ℃

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]);

Being able to obtain both reference and stamp as a single atomic operation is important for some types of concurrent algorithms.

设置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

http://tutorials.jenkov.com/java-generics/index.html

最近发表
标签列表