AtomicReference(无锁的对象引用)与AtomicStampedReference与(带有时间戳的对象引用)

时间:2022-04-08 14:33:55
AtomicReference与AtomicStampedReference


1、AtomicReference(无锁的对象引用)遇到的问题:


不懂AtomicReference,看了很多关于AtomicReference的资料,几乎都一样(都是复制的),运行基本看不到想要的结果。
自己研究了一下,分享出来。


2、使用实例:
场景:蛋糕店回馈客户,对于会员卡余额小于20的客户一次性赠送20,刺激消费,每个客户只能赠送一次。


import java.util.concurrent.atomic.AtomicReference;


public class test3 {
public static void main(String[] args) {

//在这里使用AtomicReference
final AtomicReference<Integer> money = new AtomicReference<Integer>();
// 初始卡余额小于20
money.set(19);

// 模拟多个线程更新数据库,为用户充值


for (int i = 0; i < 3; i++) {
new Thread() {
public void 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;
}


}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}

// 用户消费进程,模拟消费行为
new Thread() {


@Override
public void run() {
//在这里的for循环,太快很容易看不到结果
for (int i = 0; i < 1000; i++) {
while (true) {
Integer m = money.get();


if (m > 10) {
System.out.println("大于10元");
if (money.compareAndSet(m, m - 10)) {
System.out.println("成功消费10,卡余额:" + money.get());
break;
}
} else {
System.out.println("余额不足!");
break;
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}


}.start();
}
}

结构输出:


余额小于20,充值成功。余额:39元
余额大于20,无需充值!
大于10元
成功消费10,卡余额:29
余额大于20,无需充值!
大于10元
成功消费10,卡余额:19
大于10元
成功消费10,卡余额:9
余额大于20,无需充值!
余额小于20,充值成功。余额:29元
大于10元
成功消费10,卡余额:19
余额小于20,充值成功。余额:39元
大于10元
成功消费10,卡余额:29
大于10元
成功消费10,卡余额:19
余额大于20,无需充值!
余额小于20,充值成功。余额:39元
大于10元
成功消费10,卡余额:29
余额大于20,无需充值!
大于10元
成功消费10,卡余额:19
大于10元
成功消费10,卡余额:9


我们看到,这个帐号先后反复多次进行充值。原因是帐户余额被反复修改,修改后的值等于原来的值,使得CAS操作无法正确判断当前的数据状态。这在业务上是不允许的。

为什么会出现这样的问题: 那是因为对象值被反复修改原数据

AtomicReference(无锁的对象引用)与AtomicStampedReference与(带有时间戳的对象引用)


3、解决方案:


我们使用带有时间戳的对象引用来解决:


import java.util.concurrent.atomic.AtomicStampedReference;


public class test4 {


public static void main(String[] args) {

// final AtomicReference<Integer> money = new  AtomicReference<Integer>();
//初始卡余额小于20
// money.set(19);


//这里用AtomicStampedReference
final AtomicStampedReference<Integer> money = new AtomicStampedReference<Integer>(19, 0);


// 模拟多个线程更新数据库,为用户充值


for (int i = 0; i < 3; i++) {
final int timestamp = money.getStamp();


new Thread() {
public void run() {
while (true) {
while (true) {
Integer m = money.getReference();
if (m < 20) {
if (money.compareAndSet(m, m + 20, timestamp,
timestamp + 1)) {
System.out.println("余额小于20,充值成功。余额:"
+ money.getReference() + "元");
break;
}
} else {
System.out.println("余额大于20,无需充值!");
break;
}
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
// 用户消费进程,模拟消费行为
new Thread() {


@Override
public void run() {
for (int i = 0; i < 100; i++) {
while (true) {
int timestamp = money.getStamp();
Integer m = money.getReference();


if (m > 10) {
System.out.println("大于10元");
if (money.compareAndSet(m, m - 10, timestamp,
timestamp + 1)) {
System.out.println("成功消费10,卡余额:"
+ money.getReference());
break;
}
} else {
System.out.println("余额不足!");
break;
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}


}.start();
}
}


结果输出打印:

余额小于20,充值成功。余额:39元
余额大于20,无需充值!
余额大于20,无需充值!
大于10元
成功消费10,卡余额:29
大于10元
成功消费10,卡余额:19
大于10元
成功消费10,卡余额:9
余额不足!
余额不足!
余额不足!

可以看到账户只被赠予一次。


每天努力一点,每天都在进步!