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的基础子类的实体指向父类的引用模式使得类之间层次清楚。
一点思考
在设计工具类的时候,如果要操作的类的范围比较确定而且还具有一定的兼容性的时候,我们也可以采用这种虚类+内部实现类的方式。
- 异步加载的基本逻辑与浏览器抓包一般流程
- 左手用R右手Python系列之——表格数据抓取之道
- XML/HTML/JSON——数据抓取过程中不得不知的几个概念
- R语言网络数据抓取的又一个难题,终于攻破了!
- R语言数据清洗实战——高效list解析方案
- 左手用R右手Python系列——循环中的错误异常规避
- SpringBoot2.x开发案例之整合Quartz任务管理系统
- 给出一组非负整数,重新排序组成最大的数
- [机智的机器在学习] TensorFlow实现Kmeans聚类
- [机智的机器在学习] 利用TensorFlow实现多元线性回归分类器
- [数据结构和算法]《算法导论》动态规划笔记(1)
- [数据结构和算法]《算法导论》动态规划笔记(2)
- [算法与数据结构] 《算法导论》堆排序笔记
- [数据结构与算法] 链表的其他类型
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python 基础(十六):迭代器与生成器
- MySQL information_schema详解 COLUMNS
- MySQL information_schema详解 COLUMN_PRIVILEGES
- 一分钟学Python|Python的字典
- MySQL information_schema详解 ENGINES
- 一日一技:不用轮询,基于事件监控文件变动
- Python 基础(十七):装饰器
- XtraBackup工具详解 Part 10 使用innobackupex对数据库进行部分备份(指定表或数据库)
- Python 基础(十九):数学相关模块
- XtraBackup工具详解 Part 11 使用innobackupex对部分备份进行恢复
- XtraBackup工具详解 Part 12 流式和压缩备份
- 基于STM32+RT-Thread的新冠肺炎疫情监控平台
- Python 基础(二十):sys 模块
- [Oracle 故障处理]记一次Windows监听启动失败
- Python 基础(二十一):argparse 模块