美文网首页
突击并发编程JUC系列-原子引用AtomicReference

突击并发编程JUC系列-原子引用AtomicReference

作者: 山间木匠1 | 来源:发表于2020-09-24 12:32 被阅读0次

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

    小伙伴们,大家好,我们又见面了,突击并发编程 JUC 系列实战原子引用马上就要发车了。

    原子引用

    AtomicReference 类提供了对象引用的非阻塞原子性读写操作,对比原子更新基本类型的 AtomicInteger ,只能更新一个变量,如果要原子更新多个变量,就需要使用这个原子更新引用类型提供的类。Atomic 包提供了以下 3 个类。

    lake_card_mindmap (1).png

    原子类型常用方法如下

    方法名 说明
    V get() 以原子方式获取当前值
    void set(V newValue) 以原子方式赋值给定值
    boolean compareAndSet(V expect, V update) 如果当前值 ==为预期值,则将值设置为给定的更新值。
    boolean weakCompareAndSet(V expect, V update) 如果当前值为 == ,则将原值设置为给定的更新值,就算相等也不一定设置成功。
    V getAndSet(V newValue) 将原子设置为给定值并返回旧值。
    ........
    JDK 1.8 新增
    V getAndUpdate(UnaryOperator<V> updateFunction) 使用应用给定函数的结果原子更新当前值,返回上一个值。
    V updateAndGet(UnaryOperator<V> updateFunction) 使用给定函数的结果原子更新当前值,返回更新的值。
    V getAndAccumulate(V x, BinaryOperator<V> accumulatorFunction) 使用给定函数应用给当前值和给定值的结果原子更新当前值,返回上一个值。
    V accumulateAndGet(V x,BinaryOperator<V> accumulatorFunction) 使用将给定函数应用于当前值和给定值的结果原子更新当前值,返回更新后的值。
    ........

    小试牛刀

    为了帮助大家理解,通过教师投票数的场景,进行案例讲解,教师类被设计为不可变对象,一旦创建就无法进行修改。类中只包含两个字段名:教师名,得票数。具体教师类代码如下:

    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;
        }
    }
    

    AtomicReference 案例演示

    这是一个天理不容的场景,小春哥一直兢兢业业工作,获得无数学生的芳心,临近期末通过学生投票评选一年一度的优秀教师,懵懂少年一直嫉妒我,拿到我的信息,解锁了最佳优秀的教师信箱,并自己的票数修改了,然后将信息放入信箱。代码演示如下:

    public class AtomicExample6 {
        /**
         * 引用类型 AtomicReference
         */
        private static AtomicReference<Teacher> atomicReference = new AtomicReference<>();
        
        private static BinaryOperator<Teacher> binaryOperator = new BinaryOperator<Teacher>() {
            @Override
            public Teacher apply(Teacher teacher, Teacher teacher2) {
                // 返回新值
                return teacher2;
            }
        };
        public static void main(String[] args) {
            Teacher teacher = new Teacher("小春哥", 200);
            // 将当前对象设置到引用对象 AtomicReference 中
            atomicReference.set(teacher);
            Teacher updateTeacher = new Teacher("懵懂少年", 180);
            // teacher 和 引用类型AtomicReference 保存的对象一致 则能修改成功 
            atomicReference.compareAndSet(teacher, updateTeacher);
            System.out.println(atomicReference.get().getNamne());
            System.out.println(atomicReference.get().getTicketNum());
             Teacher accumulateTeacher = new Teacher("懵懂少年", 210);
            // 原子性地更新指定对象,并且返回AtomicReference更新后的值
            atomicReference.accumulateAndGet(accumulateTeacher, binaryOperator);
            System.out.println(atomicReference.get().getName());
            System.out.println(atomicReference.get().getTicketNum());
        }
    }
    

    被成功修改的结果如下:

    懵懂少年
    180
    懵懂少年
    210
    

    AtomicReference 除了引用对象的使用,对于基本类型也是可以进行操作的。

    AtomicReferenceFieldUpdater 案例演示

    AtomicReferenceFieldUpdater是一个基于反射的类,它允许原子更新指定类的指定voltile字段。小春哥只是暂时代课,无法参与学生投票,学校就直接将我的票数,转交给了我帮忙代课的老师了。代码演示如下:

    public class AtomicExample7 {
    
        /**
         * 引用类型 AtomicReferenceFieldUpdater
         */
        private static AtomicReferenceFieldUpdater referenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(Teacher.class, String.class, "name");
    
    
        public static void main(String[] args) {
            Teacher teacher = new Teacher("小春哥", 200);
    
            referenceFieldUpdater.compareAndSet(teacher, "小春哥", "公众号:山间木匠");
    
            System.out.println("修改后的name为:" + teacher.getName());
            System.out.println("修改后的票数为:" + teacher.getTicketNum());
    
        }
    
    }
    

    被成功修改的结果如下:

    修改后的name为:公众号:山间木匠
    修改后的票数为:200
    

    AtomicMarkableReference 案例演示

    能解决 ABA 问题???,AtomicMarkableReference 通过 boolean 值作为是否更改的标记,所以他的版本号只有 truefalse。在并发中如果两个版本号不断地切换,任然不能很好地解决 ABA 问题,只是从某种程度降低了ABA事件发生。

    当增强版信箱还没有关闭的时候,懵懂少年修改了之前的票数,修改之后害怕自己遭受质疑,通过上一次的版本又再一次修改了自己的票数。代码演示如下:

    public class AtomicExample8 {
    
        public static void main(String[] args) {
    
            Teacher teacher = new Teacher( "小春哥",200);
            AtomicMarkableReference<Teacher> markableReference = new AtomicMarkableReference<>(teacher, true);
            Teacher newTeacher = new Teacher("懵懂少年",210);
    
            System.out.println("当前返回状态: "+markableReference.compareAndSet(teacher, newTeacher, true, false));
            System.out.println("AtomicMarkableReference 状态: "+markableReference.isMarked());
            Teacher twoTeacher = new Teacher("懵懂少年",201);
            System.out.println("当前返回状态 状态: "+markableReference.compareAndSet(newTeacher, twoTeacher, false, false));
            System.out.println("AtomicMarkableReference 状态: " + markableReference.isMarked());
    
    
        }
    
    }
    

    被成功修改的结果如下:

    当前返回状态: true
    AtomicMarkableReference 状态: false
    当前返回状态 状态: true
    AtomicMarkableReference 状态: false
    

    总结

    本章节通过案例演示了原子引用AtomicReferenceAtomicReferenceFieldUpdaterAtomicMarkableReference,感兴趣的小伙伴可以通过代码进行进一步的分析。


    欢迎关注公众号 山间木匠 , 我是小春哥,从事 Java 后端开发,会一点前端、通过持续输出系列技术文章与文会友,如果本文能为您提供帮助,欢迎大家关注、 点赞、分享支持,我们下期再见!<br />

    相关文章

      网友评论

          本文标题:突击并发编程JUC系列-原子引用AtomicReference

          本文链接:https://www.haomeiwen.com/subject/ubpzyktx.html