AtomicIntegerFieldUpdater字段原子更新类
本文链接:https://blog.csdn.net/anLA_/article/details/78662383
前面讲的两个AtomicInteger和AtomicIntegerArray,这两个都是在最初设计编码时候就已经考虑到了需要保证原子性。但是往往有很多情况就是,由于需求的更改,原子性需要在后面加入,类似于我不要求你这整个类操作具有原子性,我只要求你里面一个字段操作具有原子性。没错,concurrent.atomic包下AtomicIntegerFieldUpdater就是这个作用的。
AtomicXXXFieldUpdater主要包括以下几个:AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicReferenceFieldUpdater。
What is AtomicIntegerFieldUpdater
相信前言部分讲的已经很清晰易懂了,AtomicIntegerFieldUpdater就是用来更新某一个实例对象里面的int属性的。 
但是注意,在用法上有规则:
字段必须是volatile类型的,在线程之间共享变量时保证立即可见
字段的描述类型(修饰符public/protected/default/private)是与调用者与操作对象字段的关系一致。也就是说调用者能够直接操作对象字段,那么就可以反射进行原子操作。
对于父类的字段,子类是不能直接操作的,尽管子类可以访问父类的字段。
只能是实例变量,不能是类变量,也就是说不能加static关键字。
只能是可修改变量,不能使final变量,因为final的语义就是不可修改。
对于AtomicIntegerFieldUpdater和AtomicLongFieldUpdater只能修改int/long类型的字段,不能修改其包装类型(Integer/Long)。如果要修改包装类型就需要使用AtomicReferenceFieldUpdater。
具体规则可以通过以下测试例子来分析:
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
public class AtomicIntegerFieldUpdaterAnalyzeTest {
public static void main(String[] args) {
        AtomicIntegerFieldUpdaterAnalyzeTest test = new AtomicIntegerFieldUpdaterAnalyzeTest();
        test.testValue();
    }
public AtomicIntegerFieldUpdater<DataDemo> updater(String name) {
        return AtomicIntegerFieldUpdater.newUpdater(DataDemo.class, name);
    }
public void testValue() {
        DataDemo data = new DataDemo();
//      //访问父类的public 变量,报错:java.lang.NoSuchFieldException
//      System.out.println("fatherVar = "+updater("fatherVar").getAndIncrement(data));
//      
//      //访问普通 变量,报错:java.lang.IllegalArgumentException: Must be volatile type
//      System.out.println("intVar = "+updater("intVar").getAndIncrement(data));
//      //访问public volatile int 变量,成功
//      System.out.println("publicVar = "+updater("publicVar").getAndIncrement(data));
//      
//      //访问protected volatile int 变量,成功
//      System.out.println("protectedVar = "+updater("protectedVar").getAndIncrement(data));
//      
//      //访问其他类private volatile int变量,失败:java.lang.IllegalAccessException
//      System.out.println("privateVar = "+updater("privateVar").getAndIncrement(data));
//      
//      //访问,static volatile int,失败,只能访问实例对象:java.lang.IllegalArgumentException
//      System.out.println("staticVar = "+updater("staticVar").getAndIncrement(data));
//      
//      //访问integer变量,失败, Must be integer type
//      System.out.println("integerVar = "+updater("integerVar").getAndIncrement(data));
//      
//      //访问long 变量,失败, Must be integer type
//      System.out.println("longVar = "+updater("longVar").getAndIncrement(data));
//自己在自己函数里面可以访问自己的private变量,所以如果可见,那么可以进行原子性字段更新
        data.testPrivate();
    }
}
class Father{
    public volatile int fatherVar = 4;
}
class DataDemo extends Father {
public int intVar = 4;
public volatile int publicVar = 3;
    protected volatile int protectedVar = 4;
    private volatile int privateVar = 5;
public volatile static int staticVar = 10;
    //The field finalVar can be either final or volatile, not both
    //public final volatile int finalVar = 11;
public volatile Integer integerVar = 19;
    public volatile Long longVar = 18L;
public void testPrivate(){
        DataDemo data = new DataDemo();
        System.out.println(AtomicIntegerFieldUpdater.newUpdater(DataDemo.class, "privateVar").getAndIncrement(data));
    }
实现
首先看定义:
/**
 * 允许一个已经定义的类里面的某一个volatile int型变量的原子更新。
 * 注意只能够原子更新里面的某一个int型的变量。
 * 思路是通过反射获取变量,为一个updater,然后进行更新。
 */
public abstract class AtomicIntegerFieldUpdater<T>
AtomicIntegerFieldUpdater是一个抽象类,但是它内部有一个private final类型的默认子类,所以在调用newUpdater的时候,会用模式子类来实现:
/**
     * 创建一个updater,
     * tclass:包含类的名称
     * fieldName:字段名字
     */
    @CallerSensitive
    public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,
                                                              String fieldName) {
        return new AtomicIntegerFieldUpdaterImpl<U>
            (tclass, fieldName, Reflection.getCallerClass());
    }
而除了这些之外,子类中还有判断对象访问权限,以及判断是否为父类,是否同一个包等方法:
//判断second是否为first的父类
private static boolean isAncestor(ClassLoader first, ClassLoader second) ;
//判断class1和class2是否在同一个包下
private static boolean isSamePackage(Class<?> class1, Class<?> class2)
//获得包名
private static String getPackageName(Class<?> cls)
//判断object是否为当前class的一个子类
private final void accessCheck(T obj)
另外就是一些CAS方法,实际上都是调用Unsafe.java中的native方法:
public final boolean compareAndSet(T obj, int expect, int update) {
            accessCheck(obj);
            return U.compareAndSwapInt(obj, offset, expect, update);
        }
public final boolean weakCompareAndSet(T obj, int expect, int update) {
            accessCheck(obj);
            return U.compareAndSwapInt(obj, offset, expect, update);
        }
public final void set(T obj, int newValue) {
            accessCheck(obj);
            U.putIntVolatile(obj, offset, newValue);
        }
public final void lazySet(T obj, int newValue) {
            accessCheck(obj);
            U.putOrderedInt(obj, offset, newValue);
        }
public final int get(T obj) {
            accessCheck(obj);
            return U.getIntVolatile(obj, offset);
        }
public final int getAndSet(T obj, int newValue) {
            accessCheck(obj);
            return U.getAndSetInt(obj, offset, newValue);
        }
AtomicLongFieldUpdater和AtomicReferenceFieldUpdater
在AtomicLongFieldUpdater类中,由于有些32位系统一次性无法对64位的long进行原子运算,所以为了保证安全,在这些不能一次性进行原子运算的需要区分考虑,利用加synchronized锁来实现:
@CallerSensitive
    public static <U> AtomicLongFieldUpdater<U> newUpdater(Class<U> tclass,
                                                           String fieldName) {
        Class<?> caller = Reflection.getCallerClass();
        if (AtomicLong.VM_SUPPORTS_LONG_CAS)
        //直接cas实现
            return new CASUpdater<U>(tclass, fieldName, caller);
        else
        //带synchronized锁实现
            return new LockedUpdater<U>(tclass, fieldName, caller);
    }
什么意思呢?下面给出几个具体方法:
直接CAS实现:
//直接CAS实现
        public final boolean compareAndSet(T obj, long expect, long update) {
            accessCheck(obj);
            return U.compareAndSwapLong(obj, offset, expect, update);
        }
加锁的CAS实现:
//加锁的CAS实现
        public final boolean compareAndSet(T obj, long expect, long update) {
            accessCheck(obj);
            synchronized (this) {
                long v = U.getLong(obj, offset);
                if (v != expect)
                    return false;
                U.putLong(obj, offset, update);
                return true;
            }
        }
在其他方面, AtomicLongFieldUpdater和AtomicReferenceFieldUpdater实现思想基本一致。
参考资料: 
http://blog.csdn.net/u010412719/article/details/52068888
————————————————
版权声明:本文为CSDN博主「6点A君」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/anLA_/article/details/78662383
AtomicIntegerFieldUpdater字段原子更新类的更多相关文章
- [Java] 集合框架原理之二:锁、原子更新、线程池及并发集合
		
java.util.concurrent 包是在 Java5 时加入的,与 concurrent 的相关的有 JMM及 AbstractQueuedSynchronizer (AQS),两者是实现 c ...
 - solr4.x之原子更新
		
solr4.x发布以后,最值得人关注的一个功能,就是原子更新功能,传说的solr是否能真正的做到像数据库一样,支持单列更新呢? 在solr官方的介绍中,原子更新是filed级别的更新,不会涉及整个Do ...
 - solr的原子更新/局部更新
		
solr支持三种类型的原子更新: set - to set a field. add - to add to a multi-valued field. inc - to increment a fi ...
 - Solr搜索引擎【索引提交、事务日志、原子更新】
		
一.索引提交 当一个文档被添加到Solr中,但没有提交给索引之前,这个文档是无法被搜索的.换句话说,从查询的角度看,文档直到提交之后才是可见的.Solr有两种类型的提交:软提交和正常提交[也称硬提交] ...
 - Java原子变量类需要注意的问题
		
在学习多线程时,遇到了原子变量类,它是基于 CAS 和 volatile 实现的,能够保障对共享变量进行 read-modify-write 更新操作的原子性和可见性.于是我就写了一段代码试试,自认为 ...
 - 全面了解 Java 原子变量类
		
目录 一.原子变量类简介 二.基本类型 三.引用类型 四.数组类型 五.属性更新器类型 参考资料
 - Java锁与非阻塞算法的性能比较与分析+原子变量类的应用
		
15.原子变量与非阻塞同步机制 在java.util.concurrent包中的许多类,比如Semaphore和ConcurrentLinkedQueue,都提供了比使用Synchronized更好的 ...
 - 开发笔记:基于EntityFramework.Extended用EF实现指定字段的更新
		
今天在将一个项目中使用存储过程的遗留代码迁移至新的架构时,遇到了一个问题——如何用EF实现数据库中指定字段的更新(根据UserId更新Users表中的FaceUrl与AvatarUrl字段)? 原先调 ...
 - MyBatis里json型字段到Java类的映射
		
一.简介 我们在用MyBatis里,很多时间有这样一个需求:bean里有个属性是非基本数据类型,在DB存储时我们想存的是json格式的字符串,从DB拿出来时想直接映射成目标类型,也即json格式的字符 ...
 
随机推荐
- Shell编程学习记录
			
一.shell中单引号和双引号的区别: 1).单引号属于强引用,它会忽略所有被引起来的字符的特殊处理,被引用起来的字符会被原 封不动的使用,唯一需要注意的点是不允许引用自身: 2).双引号属于弱引用, ...
 - Asp.Net Core异常处理
			
本文将介绍在ASP.Net Core中处理异常的几种方法 1使用开发人员异常页面(The developer exception page) 2配置HTTP错误代码页 Configuring stat ...
 - LeetCode第151场周赛(Java)
			
这是我第一次写周赛的题目,而且还是虚拟的.从这次起,以后就将所有错过的题目都写到博客来.当然既然是我错的,那代码肯定不是我自己的.我会注明来源.并且我会自己敲一遍.多总结总是没坏处的. 另外比较糟糕的 ...
 - 中国大学MOOC-翁恺-C语言程序设计习题集(二)
			
04-0. 求符合给定条件的整数集(15)给定不超过6的正整数A,考虑从A开始的连续4个数字.请输出所有由它们组成的无重复数字的3位数. 输入格式: 输入在一行中给出A. 输出格式: 输出满足条件的的 ...
 - 树莓派raspberrypi系统安装docker以及编译nginx和php镜像
			
前言 在树莓派中搭建php环境,按正常流程一般是直接在系统中apt-get install相关的软件,不过如果某天我想无缝迁移到另一个地方,就又得在重新安装一次环境.所以为了方便,就直接在树莓派中使用 ...
 - 关于Shareppoint客户端对象模型和Shareppoint根据内部名称获取字段值的随笔
			
实际上,每个SharePoint字段实际上有两个名称,一个是“标题”(Title,有时候也把它叫做“显示名称”),一个是“内部名称”(Internal Name).平时用户在列表视图界面上看到的,都是 ...
 - DDL和DML 的区别
			
DDL (Data Definition Language 数据定义语言) create table 创建表 alter table 修改表 drop table 删除表 truncate table ...
 - winform实现图片的滑动效果
			
使用winform实现图片的滑动效果(类似网站首页图片滑动切换效果),结果实现了,但是效果其实不是很理想.也许有更好的方法. Timer timerSlide = null; //当前 ...
 - 【方法】list<?>  两个list集合 查找不同元素,求差值
			
//方法1 //自己声明list//思路,从list1中删除list2中相同的元素//使用循环遍历对比的方式删除//list1包含list2,list1多与list2//结束得出list1为不相同元素 ...
 - Part_three:Redis持久化存储
			
redis持久化存储 Redis是一种内存型数据库,一旦服务器进程退出,数据库的数据就会丢失,为了解决这个问题,Redis提供了两种持久化的方案,将内存中的数据保存到磁盘中,避免数据的丢失. 1.RD ...