• Java多线程:AtomicIntegerFieldUpdater 原子更新字段类


    AtomicIntegerFieldUpdater 

    前面我们所讲的几个原子更新引用类型如:AtomicReference,用于整个对象的更新。但不是每次都必须更新整个对象,有可能我们只需对对象中的某个字段进行原子性修改时,那么就需要使用原子更新字段类,Atomic包提供了以下三个类:

    • AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
    • AtomicLongFieldUpdater:原子更新长整型字段的更新器。
    • AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更数据和数据的版本号,可以解决使用CAS进行原子更新时,可能出现的ABA问题。

    这几个类基本类似,我们以 AtomicIntegerFieldUpdater 作为例子详细介绍。

    不同于以往的几个原子更新类,对于 AtomicIntegerFieldUpdater 的使用稍微有一些限制和约束,约束如下:

    1. 字段必须是 volatile 类型的,在线程之间共享变量时保证立即可见。

    2. 字段的描述类型(修饰符public/protected/default/private)是与调用者与操作对象的关系一致。也就是说调用者能够直接操作对象字段,那么就可以反射进行原子操作。但是对于父类的字段,子类是不能直接操作的,尽管子类可以访问父类的字段。

    3. 只能是实例变量,不能是类变量,也就是说不能加static关键字。

    4. 只能是可修改变量,不能是final变量,因为final的语句就是不可修改。实际上final的语义与volatile是有冲突的,两个关键字不能同时存在

    5. 对于 AtomicIntegerFieldUpdate 和 AtomicLongFieldUpdate 只能修改 int/long 类型的字段,不能修改包装类型。如果要修改包装类型就需要使用 AtomicReferenceFieldUpdate。

    我们先来看 AtomicIntegerFieldUpdate 的方法列表

    // 受保护的无操作构造方法,供子类使用。
    protected AtomicIntFieldUpdater()
    
    // 以原子方式将给定值添加到此更新器管理的给定对象的字段的当前值。
    int addAndGet(T obj, int delta)
    // 如果当前值 == 预期值,则以原子方式将此更新器所管理的给定对象的字段设置为给定的更新值。
    abstract boolean compareAndSet(T obj, int expect, int update)
    // 以原子方式将此更新器管理的给定对象字段当前值减 1。
    int decrementAndGet(T obj)
    // 获取此更新器管理的在给定对象的字段中保持的当前值。
    abstract int get(T obj)
    // 以原子方式将给定值添加到此更新器管理的给定对象的字段的当前值。
    int getAndAdd(T obj, int delta)
    // 以原子方式将此更新器管理的给定对象字段当前值减 1。
    int getAndDecrement(T obj)
    // 以原子方式将此更新器管理的给定对象字段的当前值加 1。
    int getAndIncrement(T obj)
    // 将此更新器管理的给定对象的字段以原子方式设置为给定值,并返回旧值。
    int getAndSet(T obj, int newValue)
    // 以原子方式将此更新器管理的给定对象字段当前值加 1。
    int incrementAndGet(T obj)
    // 最后将此更新器管理的给定对象的字段设置为给定更新值。
    abstract void lazySet(T obj, int newValue)
    // 为对象创建并返回一个具有给定字段的更新器。
    static <U> AtomicIntFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName)
    // 将此更新器管理的给定对象的字段设置为给定更新值。
    abstract void set(T obj, int newValue)
    // 如果当前值 == 预期值,则以原子方式将此更新器所管理的给定对象的字段设置为给定的更新值。
    abstract boolean weakCompareAndSet(T obj, int expect, int update)

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

     1 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
     2 
     3 public class Demo {
     4     // 新建AtomicIntegerFieldUpdater对象,需要指明是哪个类中的哪个字段
     5     private static AtomicIntegerFieldUpdater<User> atom = AtomicIntegerFieldUpdater.newUpdater(User.class, "id");
     6 
     7     public static void main(String[] args) {
     8         User user = new User(100, 100);
     9         System.out.println("调用atom.addAndGet(user, 50)方法返回值为:" + atom.addAndGet(user, 50));
    10         System.out.println("调用后值变为:" + user);
    11 
    12         System.out.println("调用getAndDecrement(user)方法返回值为:" + atom.getAndDecrement(user));
    13         System.out.println("调用后值变为:" + user);
    14 
    15         System.out.println("调用compareAndSet(user, 149, 200)方法返回值为:" + atom.compareAndSet(user, 149, 200));
    16         System.out.println("调用后值变为:" + user);
    17     }
    18 }
    19 
    20 class User {
    21     volatile int id;
    22     volatile int age;
    23     public User(int id, int age) {
    24         this.id = id;
    25         this.age = age;
    26     }
    27     
    28     public String toString() {
    29         return "id:" + id + " " + "age:" + age;
    30     }
    31 }

    打印结果为:

    调用atom.addAndGet(user, 50)方法返回值为:150
    调用后值变为:id:150 age:100
    调用getAndDecrement(user)方法返回值为:150
    调用后值变为:id:149 age:100
    调用compareAndSet(user, 149, 200)方法返回值为:true
    调用后值变为:id:200 age:100

    那么 AtomicIntegerFieldUpdater 是如何进行原子性操作的呢?我们去源码一探究竟

    先看newUpdater()的源码如下

    1 public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName) {
    2     Class<?> caller = Reflection.getCallerClass();
    3     if (AtomicInteger.VM_SUPPORTS_LONG_CAS)
    4         return new CASUpdater<U>(tclass, fieldName, caller);
    5     else
    6         return new LockedUpdater<U>(tclass, fieldName, caller);
    7 }

    说明:newUpdater()的作用是获取一个AtomicIntegerFieldUpdater类型的对象。
    它实际上返回的是CASUpdater对象,或者LockedUpdater对象;具体返回哪一个类取决于JVM是否支持int类型的CAS函数。CASUpdater和LockedUpdater都是AtomicIntegerFieldUpdater的子类,它们的实现类似。下面以CASUpdater来进行说明。

    CASUpdater类的源码如下:

    1 public boolean compareAndSet(T obj, int expect, int update) {
    2     if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
    3     return unsafe.compareAndSwapInt(obj, offset, expect, update);
    4 }

    说明:它实际上是通过CAS函数操作。如果类的int对象的值是expect,则设置它的值为update。

    参考资料:

    Java多线程系列--“JUC原子类”05之 AtomicLongFieldUpdater原子类

    Java中的Atomic包使用指南

  • 相关阅读:
    原创:微信小程序页面跳转展示缓冲提示
    转发:微信小程序-template模板使用
    JS正则判断输入框是否仅仅含有汉字、字母和数字
    jQuery使用正则判断是否含有非法字符
    允许远程用户登录访问mysql的方法
    如何使php页面中不再出现NOTICE和DEPRECATED的错误提示
    原生php如何获取当前页面的url
    jQuery写缓存之:sessionStorage的运用,配合PHP将不同tab页的数据写入后台
    TP2.0或3.1 或者 3.2 下使用ajax+php做无刷新分页(转+自创)
    jquery中的replaceWith()和html()的区别
  • 原文地址:https://www.cnblogs.com/2015110615L/p/6750185.html
Copyright © 2020-2023  润新知