1、作用:
1、概述:是对 ”对象” 进行原子操作,用于描述的原子包规范原子变量的性质
提供了一种读和写都是原子性的对象引用变量。
原子意味着多个线程试图改变同一个AtomicReference(例如比较和交换操作)将不会使得AtomicReference处于不一致的状态。
2、 AtomicReference 和 AtomicInteger 非常类似,不同之处就在于 AtomicInteger 是对整数的封装,
而 AtomicReference 则对应普通的对象引用。也就是它可以保证你在修改对象引用时的线程安全性。
在介绍 AtomicReference 的同时,我希望同时提出一个有关原子操作的逻辑上的不足。
《 线程判断被修改对象是否可以正确写入的条件是对象的当前值和期望是否一致。这个逻辑从一般意义上来说是正确的。
但有可能出现一个小小的例外,就是当你获得对象当前数据后,在准备修改为新值前,对象的值被其他线程连续修改了2次,
而经过这2次修改后,对象的值又恢复为旧值。这样,当前线程就无法正确判断这个对象究竟是否被修改过
》
2、案例:
一般来说,发生这种情况的概率很小。而且即使发生了,可能也不是什么大问题。比如,我们只是简单得要做一个数值加法,即使在我取得期望值后,这个数字被不断的修改,只要它最终改回了我的期望值,我的加法计算就不会出错。也就是说,当你修改的对象没有过程的状态信息,所有的信息都只保存于对象的数值本身。
但是,在现实中,还可能存在另外一种场景。就是我们是否能修改对象的值,不仅取决于当前值,还和对象的过程变化有关,这时,AtomicReference 就无能为力了。
打一个比方,如果有一家蛋糕店,为了挽留客户,绝对为贵宾卡里余额小于20元的客户一次性赠送20元,刺激消费者充值和消费。但条件是,每一位客户只能被赠送一次。
现在,我们就来模拟这个场景,为了演示 AtomicReference,我在这里使用 AtomicReference 实现这个功能。首先,我们模拟用户账户余额。
1、定义用户账户余额:
static AtomicReference<Integer> money=newAtomicReference<Integer>();
money.set(19); // 设置账户初始值小于20,显然这是一个需要被充值的账户
`
2、接着,我们需要若干个后台线程,它们不断扫描数据,并为满足条件的客户充值。
// 模拟多个线程同时更新后台数据库,为用户充值
for(int i = 0 ; i < 3 ; i++) {
new Thread(){
publicvoid run() {
while(true){
while(true) {
Integer m = money.get();
if(m < 20){
if(money.compareAndSet(m, m + 20)) {
System.out.println("余额小于20元,充值成功,余额:" + money.get() + "元");
break;
}
} else {
System.out.println("余额大于20元,无需充值");
break ;
}
}
}
}
}.start();
}
上述代码第8行,判断用户余额并给予赠予金额。如果已经被其他用户处理,那么当前线程就会失败。
因此,可以确保用户只会被充值一次。
此时,如果很不幸的,用户正好正在进行消费,就在赠予金额到账的同时,他进行了一次消费,使得总金额又小于20元,并且正好累计消费了20元。使得消费、赠予后的金额等于消费前、赠予前的金额。这时,后台的赠予进程就会误以为这个账户还没有赠予,所以,存在被多次赠予的可能。下面&#