• 【AtomicReference、AtomicStampedReference】常规用法


    AtomicReference可以针对引用类型进行原子操作。

    1. 构造

    AtomicReference ar = new AtomicReference<>(new Person());
    AtomicReference ar = new AtomicReference<>();

    无参构造,相当于AutomicReference(null)

    有参构造,初始化为initValue

    2. 如果原来是A,则更新成B 

    public boolean compareAndSet(T source, T dest):source是改之前的值,Dest是改之后的值,source与当前真实值匹配了才能执行成功,返回值表示是否执行成功。

    AtomicReference ar = new AtomicReference<>(new Person());
    ar.compareAndSet(ar.get(), new Person(ar.get().getAge(), "newName"));

    这里底层判断的时候是采用的内存地址相等判断法,而不是equals方法,所以在更新值的时候,是采用复制对象的方法,而不是在原有对象的基础上修改的方式。否则自旋的意义就没有了。

    3. 取值并更新

    V getAndSet(V newValue) 返回旧值

    AtomicReference ar = new AtomicReference<>(new Person());
    ar.getAndSet(new Person(ar.get().getAge(), "newName"));
    

     

    4. 采用lambda表达式进行更新

    V getAndUpdate(UnaryOperator updateFunction) 返回旧值

    V updateAndGet(UnaryOperator updateFunction) 返回新值

    AtomicReference ar = new AtomicReference<>(new Person());
    System.out.println(ar.getAndUpdate(ele-> new Person(ele.getAge(), "newName")));
    System.out.println(ar.updateAndGet(ele-> new Person(ele.getAge() + 5, "newName")));

    此处需要注意,一定要基于原有的对象。

    5. 基于二元操作数进行更新 

    V getAndAccumulate(V x, BinaryOperator accumulatorFunction) 返回旧值

    V accumulateAndGet(V x, BinaryOperator accumulatorFunction) 返回新值

    AtomicReference ar = new AtomicReference<>(new Person());
    System.out.println(ar.getAndAccumulate(new Person(1, "temp"), (a, b) -> new Person(a.getAge() + b.getAge(), "newName")));
    System.out.println(ar.accumulateAndGet(new Person(2, "temp"), (a, b) -> new Person(a.getAge() + b.getAge(), "newName")));
    

    6. 直接更新值set()

    AtomicReference ar = new AtomicReference<>(new Person());
    ar.set(new Person(1, "temp");
    System.out.println(ar.get());
    

    7. 直接取值get() 

    AtomicReference的ABA的问题:

    当初始值为A,在cas进行时,是查看当前是否是A,这个过程并不关心中间过程,只要当前值是A即可。所以,当中间有多次改动的时候,我们无法察觉。

    AtomicStampedReference就是为了解决这个问题,他类似的给这个对象加了一个时间戳,表示对象的修改时间,这样每一次修改都会同步修改时间戳,这就可以保证时间戳是同步递增的,然后我们就知道A->B->C->A的改动了。

    1. 构造

    AtomicStampedReference reference = new AtomicStampedReference<>("Hello", 1);
    System.out.println(reference.getReference());
    System.out.println(reference.getStamp());

    此处可以通过getReference取出值,getStamp取出时间戳

    2. 如果原来是A,并且时间戳是a, 更新值为B,时间戳是b 

    AtomicStampedReference reference = new AtomicStampedReference<>("Hello", 1);
    reference.compareAndSet(reference.getReference(), "World", 2, 3); // 失败
    reference.compareAndSet(reference.getReference(), "World", 1, 2); // 成功

    必须值和时间戳同时满足,才可以更新

    3. 直接修改值和标签set()

    AtomicStampedReference reference = new AtomicStampedReference<>("Hello", 1);

    reference.set("World", reference.getStamp() + 1);

    4. 尝试修改标签attemptStamp()

    AtomicStampedReference reference = new AtomicStampedReference<>("Hello", 1);
    reference.attemptStamp(reference.getReference(), 2); 

    只有值匹配的时候,才可以修改标签

  • 相关阅读:
    Java 程序结构
    linux系统编程入门
    计算机视觉(CV)技术的优势和挑战
    HTML案例一:圣诞老人
    Linux下运行Jmeter压测
    JavaScript(五):优雅的async/await
    毕业设计2349基于jsp的网上订餐系统【程序源码+文档+调试运行】
    2022CTF培训(四)花指令&字符串混淆入门
    一分钟学一个 Linux 命令 - find 和 grep
    封装图片处理工具类,实现图片的裁剪、压缩、图片水印、文字水印、多行文字水印等功能
  • 原文地址:https://blog.csdn.net/Day_and_Night_2017/article/details/126277681