AtomicIntegerFieldUpdater源码解析

时间:2022-07-23
本文章向大家介绍AtomicIntegerFieldUpdater源码解析,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

之前我们看了AtomicInteger和AtomicIntegerArray的源码,今天主要解析一下AtomicIntegerFieldUpdater,从字面上看这个类的主要作用就是原子性质的更新Integer类型的属性。在看源码之前,我们还是先猜一下这个类的实现方式是怎样的。由于Atomic包都是基于CAS进行操作的,而CAS在java中是采用sun包提供的Unsafe,Unsafe方法是直接操作内存的。而对于一个对象来说通过操作对象实体所在的内存来操作对象的中的属性,那么肯定需要记录属性的地址相对对象实体的偏移量,然后就可以直接采用CAS来进行数据的操作了,但是java的对象是线程公用的,因此其中的属性也存在多线程问题,所以其中的属性字段也应该是volatile。

AtomicIntegerFieldUpdater的使用方式

AtomicIntegerFieldUpdater需要通过先创建特定类的更新器,然后使用这个更新器再去针对该类的Integer字段进行操作,所以AtomicIntegerFieldUpdater其实就是一个针对对象中Integer属性操作的工具类。

源码学习

在newUpdater方法中,我们看到参数分别为工具类要操作的类路径和类的属性字段名称。根据开始的猜测,那么这里的AtomicIntegerFieldUpdateImple的主要作用就是寻找到tclass类的内存地址和fieldName的偏移量。Reflection.getCallerClass()则是获取当前运行的类的类路径了。

注意这里的@CallerSensitive和Reflection.getCallerClass()。@CallerSensitive主要是为了避免Reflection的参数,而且是为了修改jdk中利用双重反射的越权漏洞。

对于这个注解此处要明白它在这主要是配合Reflection.getCallerClass() 作用是避免自己写Reflection.getCallerClass()的参数 增加这个特性主要还是为了修复一个jdk中的利用双重反射的越权漏洞 https://www.jianshu.com/p/8c5a2e143cc6

继续查看AtomicIntegerFieldUpdateImple的类结构

其中有我们熟悉的Unsafe,offset字段。其中的field就是需要更新的integer类型的字段。AccessController.doPrivileged就是获取当前需要被操作的字段。

field.getModifiers()的作用是获取字段的修饰符。

sun.reflect.misc.ReflectUtil.ensureMemberAccess(
                    caller, tclass, null, modifiers)

方法ensureMemberAccess主要是判断是否可以访问传入的类的字段。

//获取传入类的类加载器
ClassLoader cl = tclass.getClassLoader();
//获取调用类的类加载器
ClassLoader ccl = caller.getClassLoader();
   if ((ccl != null) && (ccl != cl) &&((cl == null) || !isAncestor(cl, ccl))) {
       //如果这两个类的类加载器没有委派关系则进行包可访问性
       sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
       }

isAncestor方法主要是通过加载器的委派模型不断的去寻找父加载器。判断两个类加载器是否有委派关系。

        private static boolean isAncestor(ClassLoader first, ClassLoader second) {
            ClassLoader acl = first;
            do {
                acl = acl.getParent();
                if (second == acl) {
                    return true;
                }
            } while (acl != null);
            return false;
        }
//必须要为int
if (field.getType() != int.class)
            throw new IllegalArgumentException("Must be integer type");
//操作的属性必须要用volative
            if (!Modifier.isVolatile(modifiers))
                throw new IllegalArgumentException("Must be volatile type");
//如果是protected修饰,目标类是调用类的父类,调用类和目标类不是同一个包下时,cclass存储调用者的类
//否则cclass存储为tclass
            this.cclass = (Modifier.isProtected(modifiers) &&
                           tclass.isAssignableFrom(caller) &&
                           !isSamePackage(tclass, caller))
                          ? caller : tclass;
            this.tclass = tclass;
            //获取字段的偏移量
            this.offset = U.objectFieldOffset(field);

这里记录tclass的意义就是在使用的时候判断传入的对象是否为tclass的实体。在AtomicIntegerFieldUpdater使用CAS去修改对象的值时,都会通过accessCheck去判断

   private final void accessCheck(T obj) {
            if (!cclass.isInstance(obj))
                throwAccessCheckException(obj);
        }

如果不是cclass类的实体,那么就会抛出异常,并进行详细的错误说明。

 private final void throwAccessCheckException(T obj) {
            if (cclass == tclass)
                throw new ClassCastException();
            else
                throw new RuntimeException(
                    new IllegalAccessException(
                        "Class " +
                        cclass.getName() +
                        " can not access a protected member of class " +
                        tclass.getName() +
                        " using an instance of " +
                        obj.getClass().getName()));

这里要说明的是AtomicIntegerFieldUpdater是一个虚类,AtomicIntegerFieldUpdaterImpl继承了AtomicIntegerFieldUpdater并实现了AtomicIntegerFieldUpdater提供的虚函数,在newUpdater的时候返回了AtomicIntegerFieldUpdaterImpl的实体,所以我们的进行操作的时候实际上使用的AtomicIntegerFieldUpdaterImpl对象,继而进行数据的CAS操作。

除此之外,AtomicIntegerFieldUpdater还将java8提供的lamda表达式直接放置到AtomicIntegerFieldUpdater中,而将计算结果的赋值操作放置到AtomicIntegerFieldUpdaterImpl中,巧妙的利用了java的基础子类的实体指向父类的引用模式使得类之间层次清楚。

一点思考

在设计工具类的时候,如果要操作的类的范围比较确定而且还具有一定的兼容性的时候,我们也可以采用这种虚类+内部实现类的方式。