AtomicReference 这个类和AtomicInteger非常类似,只是AtomicReference对应普通的对象引用,而AtomicInteger 它是对整数的封装,它的方法如下

4.4.4 无锁的对象引用:AtomicReference和AtomicStampedReference-LMLPHP

对weakCompareAndSet 说明:

第一次看weakCompareAndSet doc文档的说明时,我是困惑的。我并不清楚你说的“fail spuriously”和“not provide ordering guarantees”的确切含义。于是我查询了些相关资料。

首先,我从jdk 8 的官方文档的java.util.concurrent.atomic上找到这么二段话:

一个原子类也支持weakCompareAndSet方法,该方法有适用性的限制。在一些平台上,在正常情况下weak版本比compareAndSet更高效,但是不同的是任何给定的weakCompareAndSet方法的调用都可能会返回一个虚假的失败( 无任何明显的原因 )。一个失败的返回意味着,操作将会重新执行如果需要的话,重复操作依赖的保证是当变量持有expectedValue的值并且没有其他的线程也尝试设置这个值将最终操作成功。( 一个虚假的失败可能是由于内存冲突的影响,而和预期值(expectedValue)和当前的值是否相等无关 )。此外weakCompareAndSet并不会提供排序的保证,即通常需要用于同步控制的排序保证。然而,这个方法可能在修改计数器或者统计,这种修改无关于其他happens-before的程序中非常有用。当一个线程看到一个通过weakCompareAndSet修改的原子变量时,它不被要求看到其他变量的修改,即便该变量的修改在weakCompareAndSet操作之前。

weakCompareAndSet实现了一个变量原子的读操作和有条件的原子写操作,但是它不会创建任何happen-before排序,所以该方法不提供对weakCompareAndSet操作的目标变量以外的变量的在之前或在之后的读或写操作的保证。

这二段话是什么意思了,也就是说weakCompareAndSet底层不会创建任何happen-before的保证,也就是不会对volatile字段操作的前后加入内存屏障。因为就无法保证多线程操作下对除了weakCompareAndSet操作的目标变量( 该目标变量一定是一个volatile变量 )之其他的变量读取和写入数据的正确性。

AtomicReference 这个类有个问题 就是 如果有2个线程改变了值,最后改回它获取的值,那么程序也会继续执行,可能造成重复执行。例如:为账户低于多少钱用户赠送仅仅-张优惠卷,如果他在获得一张之后,充值了钱又花费了,就会造成多次操作。测试类:

public class AtomicInteget {
public static void main(String[] args) {
AtomicReference<Integer> atomicReference=new AtomicReference<Integer>();
Integer integer = atomicReference.get();
boolean b = atomicReference.compareAndSet(integer, 5);
System.out.println(b);
boolean b1 = atomicReference.compareAndSet(integer, 6);
System.out.println(b1);
boolean b2 = atomicReference.compareAndSet(atomicReference.get(), null);
System.out.println(b2);
System.out.println(atomicReference.get());
boolean b3 = atomicReference.compareAndSet(integer, 6);
System.out.println(b3);
System.out.println(integer);
System.out.println(atomicReference.get());
}
}
结果如下:

true
false
true
null
true
null
6

jdk针对于这种情况,有一个类专门处理这种问题:

4.4.4 无锁的对象引用:AtomicReference和AtomicStampedReference-LMLPHP

AtomicStampedReference<Integer> money = new AtomicStampedReference<Integer>(19, 0);

    public static void main(String args[]) {

        for (int i = 0; i < 100; i++) {
final int timestap = money.getStamp();
new Thread() {
public void run() {
while (true) {
Integer m = money.getReference();
if (m < 20) {
if (money.compareAndSet(m, m + 20, timestap, timestap + 1)) {
System.out.println("余额小于20元,充值成功,余额:" + money.getReference() + "元");
break;
}
} else {
System.out.println("余额大于20,无需充值");
break;
}
}
}
}.start();
} new Thread() {
public void run() {
for (int i = 0; i < 100; i++) { while (true) {
int timestap = money.getStamp();
Integer m = money.getReference();
if (m > 10) {
System.out.println("金额大于10元");
if (money.compareAndSet(m, m - 10, timestap, timestap + 1)) {
System.out.println("成功消费10元,余额:" + money.getReference() + "元");
break;
}
} else {
System.out.println("没有足够的金额");
break;
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start(); }
}
05-11 19:20