AtomicStampedReference

本文翻译自http://tutorials.jenkov.com/java-util-concurrent/atomicstampedreference.html,人工翻译,仅供学习交流。

AtomicStampedReference

AtomicStampedReference类提供了一个以原子形式读写的对象引用变量。原子性意味着多个线程试图改变同一的AtomicStampedReference时,AtomicStampedReference最终会处于一个一致的状态。AtomicStampedReference与AtomicReference不同之处在于AtomicStampedReference在内部同时保存一个对象引用和一个时间戳。引用和戳可以通过compareAndSet()方法使用原子性的比较和交换操作进行交换。AtomicStampedReference被设计为解决A-B-A问题,A-B-A问题将在本文后面解释。

创建AtomicStampedReference

你可以像这样创建一个AtomicStampedReference实例:

Object initialRef   = null;
int    initialStamp = 0;
AtomicStampedReference atomicStampedReference =
    new AtomicStampedReference(intialRef, initialStamp);
创建类型化AtomicStampedReference

您可以使用Java泛型创建一个类型化的AtomicStampedReference。下面是一个类型化的AtomicStampedReference示例:

String initialRef   = null;
int    initialStamp = 0;

AtomicStampedReference<String> atomicStampedReference =
    new AtomicStampedReference<String>(
        initialRef, initialStamp
    );

本例中创建的Java AtomicStampedReference只接受对String 实例的引用。建议总是使用泛型类型和AtomicStampedReference,如果你知道保存在其中的引用类型。

获取AtomicStampedReference引用

您可以使用AtomicStampedReference的get()方法来获取存储在AtomicStampedReference中的引用。如果你有一个非类型化的AtomicStampedReference,那么getReference()方法返回一个Object 类型引用。如果你有一个类型化的AtomicStampedReference,当你创建这个类时,getReference()返回对您在AtomicStampedReference变量上声明的类型的引用。
下面是第一个非类型化的AtomicStampedReference getReference()示例:

String initialRef = "first text";
AtomicStampedReference atomicStampedReference = (String)
    new AtomicStampedReference(initialRef, 0);
String reference = atomicStampedReference.getReference();

注意将get()返回的引用必须转换为String类型,因为当AtomicStampedReference是非类型化时,get()返回才是Object引用。下面是一个类型化的AtomicStampedReference示例:

String initialRef = "first text";

AtomicStampedReference<String> atomicStampedReference =
    new AtomicStampedReference<String>(
        initialRef, 0
    );

String reference = atomicStampedReference.getReference();

注意,不再需要对get()返回的引用进行强制转换,因为编译器知道它将返回一个String引用。

获取AtomicStampedReference戳记

AtomicStampedReference还包含一个getStamp()方法,可以用来获取存储内部的是stamp变量。下面是一个getStamp()的例子:

String initialRef = "first text";

AtomicStampedReference<String> atomicStampedReference =
    new AtomicStampedReference<>(initialRef, 0);

int stamp = atomicStampedReference.getStamp();

以原子方式获得引用和戳记

您可以从一个AtomicStampedReference中获得引用和戳记,使用get()方法的原子操作。get()方法将引用作为方法的返回值返回。戳记被插入到int[]数组中,该数组作为参数传递给get()方法。下面是一个get()示例:

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时,在一个类型化的AtomicStampedReference中,set()方法接受声明为其类型的任何类型作为参数。下面是一个AtomicStampedReference set()的例子:

AtomicStampedReference<String> atomicStampedReference =
     new AtomicStampedReference<>(null, 0);


String newRef = "New object referenced";
int    newStamp = 1;

atomicStampedReference.set(newRef, newStamp);

对于非类型化引用或类型化引用,set()方法的使用没有区别。你将经历的唯一真正的不同是编译器将限制您可以在类型化AtomicStampedReference上设置的类型。

比较并设置AtomicStampedReference引用

AtomicStampedReference类包含一个名为compareAndSet()的有用方法。compareAndSet()方法可以比较存储在AtomicStampedReference实例中的引用。如果它们两个引用是相同的(不使用 equals(),使用 ==进行比较),然后可以在AtomicStampedReference实例上设置一个新的引用。
如果compareAndSet()在AtomicStampedReference中设置一个新的引用,compareAndSet()方法返回true。否则,compareAndSet()返回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,所以compareAndSet()调用失败。
最后的compareAndSet()调用将成功,因为期望的引用是newRef,而期望的戳记是newStamp。

AtomicStampedReference和A-B-A问题

AtomicStampedReference是为了解决A-B-A问题而设计的。a -B- a问题是当一个引用从指向a,然后指向B,然后再指向a时。当使用比较和交换操作以原子方式更改引用时,确保只有一个线程可以将引用从旧引用更改为新引用,探测A-B-A的情况是不可能的。
非阻塞算法中会出现A-B-A问题。非阻塞算法经常使用对被保护数据结构的持续修改的引用,通知其他线程当前正在进行修改。如果线程1看到没有正在进行的修改(引用指向null),另一个线程可能提交一个修改(引用现在是非空的),完成修改并将引用交换回空,而不需要线程1检测它。A-B-A问题是如何在非阻塞算法中发生的,在我的非阻塞算法教程中有更详细的解释。
通过使用AtomicStampedReference代替AtomicReference,可以检测A-B-A问题。线程1可以使用get()从AtomicStampedReference中复制引用和标记。如果另一个线程将引用从A更改为B,然后再返回到A,然后戳记就会发生变化(如果线程明智地更新戳记——例如增加它)。
下面的代码显示了如何使用AtomicStampedReference检测A-B-A情况:

int[] stampHolder = new int[1];
Object ref = atomicStampedReference.get(stampHolder);

if(ref == null){
    //prepare optimistic modification
}

//if another thread changes the
//reference and stamp here,
//it can be detected

int[] stampHolder2 = new int[1];
Object ref2 = atomicStampedReference.get(stampHolder);

if(ref == ref2 && stampHolder[0] == stampHolder2[0]){
    //no modification since optimistic modification was prepared

} else {
    //retry from scratch.
}
下一节:AtomicIntegerArray
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值