原子引用与ABA问题的解决

1、原子引用

1.1 AtomicReference

java.util.concurrent.atomic包中不仅有原子基本类型像AtomicInteger、AtomicBoolean等基本原子数据类型。

如果我们有像User类、Org类这种引用数据类型,那么JUCA包中也为我们提供了AtomicReference原子引用。具体使用如下。

    User u = new User();
    //原子引用
    AtomicReference<User> atomicReference = new AtomicReference<User>(u);

这样我们便可获得一个原子User类atomicReference.

如下demo便可以实现User对象操作的原子性。但是还是会存在ABA问题。

public class UnsafeJH {

    static User u = new User();
    //原子引用
    static AtomicReference<User> atomicReference = new AtomicReference<User>(u);
    
    public static void main(String[] args){
        User user = new User();
        atomicReference.compareAndSet(u,user);
    }
}

1.2  带版本号的原子引用类 AtomicStampedReference

创建一个版本号的初始版本号为1的User对象。

static User u = new User();
//原子引用
static AtomicStampedReference<User> atomicStampedReference = newAtomicStampedReference<User>(u,1);

2、使用AtomicStampedReference解决ABA问题

AtomicStampedReference中维护一个版本号,ABA问题可以通过每次更新操作递增一个版本号,来确认是否当前内存中的值是否是被修改过的。

public class UnsafeJH {

    //原子引用
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<Integer>(100,1);

    public static void main(String[] args){
       new Thread(()->{
           int version = atomicStampedReference.getStamp();
           System.out.println(Thread.currentThread().getName()+"\t第一次的版本号:"+version);
           try {
               TimeUnit.SECONDS.sleep(1);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
           System.out.println(Thread.currentThread().getName()+"\t第2次的版本号:"+atomicStampedReference.getStamp());
           atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
           System.out.println(Thread.currentThread().getName()+"\t第3次的版本号:"+atomicStampedReference.getStamp());
           },"t1").start();

        new Thread(()->{
            int version = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"\t第一次的版本号:"+version);
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean b =  atomicStampedReference.compareAndSet(100,999,version,version+1);
            System.out.println(Thread.currentThread().getName()+"修改成功否"+b+"\t当前值:"+atomicStampedReference.getReference());
        },"t2").start();

    }

运行结果:

t1	第一次的版本号:1
t2	第一次的版本号:1
t1	第2次的版本号:2
t1	第3次的版本号:3
t2修改成功否false	当前值:100

线程t1获取版本号之后,线程sleep1秒确保线程t2获取到的版本号与t1的一致。

线程t2获取到版本号之后,线程sleep3秒确保t1可以完成一组ABA操作。

最后发现t2的compareAndSet方法执行失败。因为当前的版本号已被t1改为了3,与预期的1不符,所以更新失败。

这样我们就可以使用带版本号的原子引用类型AtomicStampedReference类避免ABA的发生。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值