本文链接: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字段原子更新类的更多相关文章

  1. [Java] 集合框架原理之二:锁、原子更新、线程池及并发集合

    java.util.concurrent 包是在 Java5 时加入的,与 concurrent 的相关的有 JMM及 AbstractQueuedSynchronizer (AQS),两者是实现 c ...

  2. solr4.x之原子更新

    solr4.x发布以后,最值得人关注的一个功能,就是原子更新功能,传说的solr是否能真正的做到像数据库一样,支持单列更新呢? 在solr官方的介绍中,原子更新是filed级别的更新,不会涉及整个Do ...

  3. solr的原子更新/局部更新

    solr支持三种类型的原子更新: set - to set a field. add - to add to a multi-valued field. inc - to increment a fi ...

  4. Solr搜索引擎【索引提交、事务日志、原子更新】

    一.索引提交 当一个文档被添加到Solr中,但没有提交给索引之前,这个文档是无法被搜索的.换句话说,从查询的角度看,文档直到提交之后才是可见的.Solr有两种类型的提交:软提交和正常提交[也称硬提交] ...

  5. Java原子变量类需要注意的问题

    在学习多线程时,遇到了原子变量类,它是基于 CAS 和 volatile 实现的,能够保障对共享变量进行 read-modify-write 更新操作的原子性和可见性.于是我就写了一段代码试试,自认为 ...

  6. 全面了解 Java 原子变量类

    目录   一.原子变量类简介  二.基本类型  三.引用类型  四.数组类型  五.属性更新器类型  参考资料

  7. Java锁与非阻塞算法的性能比较与分析+原子变量类的应用

    15.原子变量与非阻塞同步机制 在java.util.concurrent包中的许多类,比如Semaphore和ConcurrentLinkedQueue,都提供了比使用Synchronized更好的 ...

  8. 开发笔记:基于EntityFramework.Extended用EF实现指定字段的更新

    今天在将一个项目中使用存储过程的遗留代码迁移至新的架构时,遇到了一个问题——如何用EF实现数据库中指定字段的更新(根据UserId更新Users表中的FaceUrl与AvatarUrl字段)? 原先调 ...

  9. MyBatis里json型字段到Java类的映射

    一.简介 我们在用MyBatis里,很多时间有这样一个需求:bean里有个属性是非基本数据类型,在DB存储时我们想存的是json格式的字符串,从DB拿出来时想直接映射成目标类型,也即json格式的字符 ...

随机推荐

  1. Nvidia Jetson TX2开发板学习历程( 2 )- 更换pip源,提高下载速度

    通过将pip的源更换为国内源,来提高下载速度,这也将成为今后学习过程下载Python包的基础,建议前期一定要完成! 知名的国内源 清华:https://pypi.tuna.tsinghua.edu.c ...

  2. Find the median(线段树+离散化)(2019牛客暑期多校训练营(第七场))

    题目出处:Find the median 示例: 输入: 53 1 4 1 5 92 7 1 8 2 9 输出:3 4 5 4 5 说明:L = [3, 2 ,4, 1, 7],R = [4, 8, ...

  3. 【Linux】一步一步学Linux——Bash常用快捷键(11)

    目录 00. 目录 01. 编辑命令 02. 搜索命令 03. 控制命令 04. 其它 05. 参考 00. 目录 @ 生活在 Bash Shell 中,熟记以下快捷键,将极大的提高你的命令行操作效率 ...

  4. THUPC&CTS&APIO2019:Far Away

    流水账~ THUPC nmdwsmduliu! THUPC Day -INF~Day -2 大概就是自己做题和每周两次的考试,lsy和fcw两个外校的来吊打我们qwqqq THUPC Day -1 Z ...

  5. TJOI2019

    TJOI出一堆模板题还玩一堆梗是什么鬼 甲苯先生的字符串(矩阵快速幂) 矩阵快速幂模板题 代码 甲苯先生的滚榜(树状数组.线段树) 最开始想平衡树搞,但是平衡树太难写了 一次答案的查询相当于查询比当前 ...

  6. Java JDK1.8源码学习之路 2 String

    写在最前 String 作为我们最常使用的一个Java类,注意,它是一个引用类型,不是基本类型,并且是一个不可变对象,一旦定义 不再改变 经常会定义一段代码: String temp = " ...

  7. rabbitMq 学习笔记(二) 备份交换器,过期时间,死信队列,死信队列

    备份交换器 备份交换器,英文名称为 Altemate Exchange,简称庙,或者更直白地称之为"备胎交换器". 生产者在发送消息的时候如果不设置 mandatory 参数, 那 ...

  8. 【开发笔记】- MD5加密

    主要用于对用户密码的加密,保护用户账户安全: /** * @author shenruihai * */ import java.security.MessageDigest; import org. ...

  9. 页面、 ajax 、mock

    页面1: //html <form action = "" method="post"  name="loginForm"> & ...

  10. Java中map接口 遍历map

    转自:https://www.cnblogs.com/wjk921/p/4918442.html java集合框架用于存储数据,也被称为集合类 位于java.util包下 java.util包下常用接 ...