关于后端:突击并发编程JUC系列原子更新字段类-AtomicStampedReference

5次阅读

共计 3491 个字符,预计需要花费 9 分钟才能阅读完成。

突击并发编程 JUC 系列演示代码地址:
https://github.com/mtcarpenter/JavaTutorial

他来了,他来了,他带着 ABA 问题走来了,小伙伴们,大家好,咱们又见面了,突击并发编程 JUC 系列实战原子更新数组类马上就要发车了。

字段类型

如果须要原子地更新某个类里的某个字段时,就须要应用原子更新字段类,Atomic 包提供了以下 3 个类进行原子字段更新。

小试牛刀

AtomicIntegerFieldUpdater 案例演示

原子地更新整形字段类,还是那个糊涂少年,在批改本人的票数。
老师类如下:

public class Teacher {
    /**
     * 老师名称
     */
    public volatile String name;

    /**
     * 学生投票数
     */
    public volatile int ticketNum;

    public Teacher(String name, int ticketNum) {
        this.name = name;
        this.ticketNum = ticketNum;
    }

    public String getName() {return name;}

    public int getTicketNum() {return ticketNum;}
}


public class AtomicExample8 {private static AtomicIntegerFieldUpdater<Teacher> integerFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Teacher.class, "ticketNum");

    public static void main(String[] args) {
        // 设置糊涂少年 180 票
        Teacher teacher = new Teacher("糊涂少年", 180);
        // 180
        System.out.println("getAndIncrement =" + integerFieldUpdater.getAndIncrement(teacher));
       
        System.out.println("get=" + integerFieldUpdater.get(teacher));
    }
}
//getAndIncrement = 180
//get=181

要想原子地更新字段类须要两步。

  • 第一步,因为原子更新字段类都是抽象类,每次应用的时候必须应用静态方法 newUpdater()创立一个更新器,并且须要设置想要更新的类和属性。
  • 第二步,更新类的字段(属性)必须应用 public volatile 修饰符。

AtomicIntegerFieldUpdaterAtomicIntegerFieldUpdater 办法根本相似,办法 getAndIncrement()getAndDecrement()getAndAdd() 等办法,跟后面的 Atomiclong实现也根本类同。

AtomicStampedReference 案例演示

CAS 后面咱们都晓得,比拟替换,要更新的变量和预期值相等,则批改为预期预期值,否则批改失败。所以 CAS 就产生了经典的 ABA 问题,什么是 ABA 问题呢?比方一个值原来是 A,变成了 B,起初又变成了 A,那么 CAS 查看时会发现它的值没有发生变化,然而实际上却是产生了变动的。

加深印象

大学选课小伙伴应该深有体会,一到选课零碎卡,解体重启还在卡,多人选课多人卡,除了领取都会卡。当初有一门网红网课,课程人数限度为 100 人,这门课程相当火爆,选课零碎开启后,半分钟这门课程就达到了 99 人,还残余 1 个名额。 学生 1学生 2同时进入零碎,并点击了该课程 抉择课程 按钮,每一个名额都有一个相应的版本号进行管制,学生 2 在弹框呈现的时候校园网忽然断了,学生 1 就轻松选到了课程,然而 学生 1 发现心仪的女生没有选到此课程,就退掉了此课程,学生 2 网络好了,界面也呈现尽管还剩下一个名额,然而已被 学生 1 操作过,学生 2 拿出上次的版本号,曾经无奈抉择课程。

public class AtomicExample9 {private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(99, 1);


    public static void main(String[] args) {Thread student1 = new Thread(() -> {int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "--- 首次 stamp:" + stamp);

            atomicStampedReference.compareAndSet(99, 100,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "--- 第二次 stamp:" + atomicStampedReference.getStamp());
            atomicStampedReference.compareAndSet(100, 99,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "--- 第三次 stamp:" + atomicStampedReference.getStamp());
        }, "student1");

        Thread student2 = new Thread(() -> {int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "--- 第一次 stamp:" + stamp);
            try {System.out.println(Thread.currentThread().getName() + "--- 你的校园网正在尝试从新连贯......");
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {e.printStackTrace();
            }
            boolean result = atomicStampedReference.compareAndSet(99, 100,
                    stamp, stamp + 1);
            System.out.println(Thread.currentThread().getName() + "--- 批改胜利与否:" + result + "以后 stamp:" + atomicStampedReference.getStamp());
            System.out.println(Thread.currentThread().getName() + "--- 以后课程已选人数:" + atomicStampedReference.getReference());
        }, "student2");

        student1.start();
        student2.start();}


}
student1--- 首次 stamp:1
student1--- 第二次 stamp:2
student1--- 第三次 stamp:3
student2--- 第一次 stamp:3
student2--- 你的校园网正在尝试从新连贯......
student2--- 批改胜利与否:true  以后 stamp:4
student2--- 以后课程已选人数:100

总结

本章简略了介绍了 AtomicIntegerFieldUpdater 原子的更新某一个字段,AtomicStampedReference 能够用于解决 JUC 中的 ABA问题,比照前一个章节 AtomicStampedReference 只有 truefalse 两种抉择。在并发中如果两个版本号一直地切换,依然不能很好地解决 ABA 问题,只是从某种程度升高了 ABA 事件产生。而应用 AtomicStampedReference 就能够防止这样的问题。


欢送关注公众号 山间木匠, 我是小春哥,从事 Java 后端开发,会一点前端、通过继续输入系列技术文章与文会友,如果本文能为您提供帮忙,欢送大家关注、点赞、分享反对,_咱们下期再见!

正文完
 0