这里看一下原子数组操作和一些其他的原子操作。

AtomicIntegerArray/AtomicLongArray/AtomicReferenceArray的API类似,选择代表性的AtomicIntegerArray来描述这些问题。

int get(int i) //获得当前位置的值
void set(int i, int newValue) //设置给定位置的值
void lazySet(int i, int newValue)
int getAndSet(int i, int newValue)
boolean compareAndSet(int i, int expect, int update)
boolean weakCompareAndSet(int i, int expect, int update)
int getAndIncrement(int i)
int getAndDecrement(int i)
int getAndAdd(int i, int delta)
int incrementAndGet(int i)
int decremnetAndGet(int i)
int addAndGet(int i, int delta)

这些API和AtomicInteger是类似的,区别是这里是数组操作,所以多个索引参数。

由于这个是数组操作,就存在数组越界的问题(IndexOutBoundsException异常),所以在get/set方法前都会检查int index是否合法。先来看看该类的主要成员.

    private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final int base = unsafe.arrayBaseOffset(int[].class);
private static final int shift;
private final int[] array;

Unsafe.getUndafe()就不说了,CAS操作少不了他;

base :通过Unsafe获得数组的基址;

shift : 数组每个元素在内存的偏移量;

array : 底层实际操作数组;

  static {
int scale = unsafe.arrayIndexScale(int[].class); //数组元素的大小,必须为2^x大小
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
shift = 31 - Integer.numberOfLeadingZeros(scale); //数组元素的bit偏移量
}

数组index检查:

   private long checkedByteOffset(int i) {
if (i < 0 || i >= array.length) //索引index越界。throw 异常
throw new IndexOutOfBoundsException("index " + i); return byteOffset(i);
} private static long byteOffset(int i) { //取得指定index元素的内存位置(base + offset)
return ((long) i << shift) + base;
}

set/get时进行index检查:

 public final int get(int i) {
return getRaw(checkedByteOffset(i));
} public final void set(int i, int newValue) {
unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
}

<********************************************************字段*********************************************************************>

AtomicIntegerFieldUpdater<T>/AtomicLongFieldUpdater<T>/AtomicReferenceFieldUpdate<T,V>

上面三种是基于反射的原子更新字段的值。

相应的API也是比较简单,但是也是有一些约束的。

  1. 字段必须是volatile类型的!
  2. 字段的描述类型(修饰符public/protected/default/private)作用于调用者与操作对象的关系。即调用者能够直接操作对象字段,那么就可以反射进行原子操作证。private类型字段,调用者无法访问,更新该变量,protected类型变量成员,当操作对象为调用者class的实例或者子类时,可以访问,原子更新protected成员。
  3. 只能是实例变量,不能是类变量,也就是说不能加static关键字。
  4. 只能是可修改变量,不能使用final变量,因为final的语义就是不可修改。实际上final语义和volatile是由冲突的,这两关键字不能同时存在
  5. 对于AtomicIntegerFieldUpdater和AtomicLongFieldUpdater只能修改int/long类型字段,不能修改其包装器类型(Integer/Long)。如果要修改包装器类型需要使用AtomicReferenceFieldUpdater。

   

      以AtomicIntegerFieldUpdater为例:

public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName) {
return new AtomicIntegerFieldUpdaterImpl<U>(tclass, fieldName, Reflection.getCallerClass());
}

AtomicIntegerFieldUpdater为一个抽象类,通过static newUpdater()方法获得其实现类实例,参数为操作对象Class对象,和其变量成员名:

public abstract class  AtomicIntegerFieldUpdater<T>

private static class AtomicIntegerFieldUpdaterImpl<T> extends AtomicIntegerFieldUpdater<T>

AtomicIntegerFieldUpdater的抽象方法定义如下:

    public abstract boolean compareAndSet(T obj, int expect, int update);

    public abstract boolean weakCompareAndSet(T obj, int expect, int update);

    public abstract void set(T obj, int newValue);

    public abstract void lazySet(T obj, int newValue);

    public abstract int get(T obj);

再来看看其实现类内部:

        private final long offset; //成员变量的内存偏移量
private final Class<T> tclass; //操作对象的class对象
private final Class cclass; //调用者class对象

在进行成员更新访问时,都必须进行所谓的访问权限检查,上面几点以说明:

sun.reflect.misc.ReflectUtil.ensureMemberAccess( //成员变量访问权限的确定(排除private)
        caller, tclass, null, modifiers);
  sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass); //包访问权限

 Class fieldt = field.getType();
if (fieldt != int.class)
throw new IllegalArgumentException("Must be integer type"); //变量成员的类型必须为int if (!Modifier.isVolatile(modifiers)) //变量成员必须要关键字volatile修饰。
throw new IllegalArgumentException("Must be volatile type"); this.cclass = (Modifier.isProtected(modifiers) && //1.当成员为protected时,赋值 cclass = caller(赋值调用者class对象) 2:不为protected时,赋值 cclass = null.
caller != tclass) ? caller : null;
 private void fullCheck(T obj) {
if (!tclass.isInstance(obj)) //操作对象不为newUpdate()传入的class的实例或子类时,throw.
throw new ClassCastException();
if (cclass != null) //上面以分析,当成员没有proteced修饰时, cclass 为null,所以不要进一步检查,直接放行,
ensureProtectedAccess(obj);
}

当变量为proteced修饰时:

 private void ensureProtectedAccess(T obj) {
if (cclass.isInstance(obj)) { //当要原子操作的对象obj为调用者class的实例或者子类时,放行,运行原子操作,否则Throw。
return;
}
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()
)
);
}

AtomicMarkableReference类描述的一个<Object, Boolean>的pair,可以原子的修改object或者boolean的值,这种数据结构在一些缓存或者章台描述中比较有用。这种结构在单个或者同时修改Object/Boolean的时候能够有效的提高吞吐量。

private static class Pair<T> {
final T reference;
final boolean mark;
private Pair(T reference, boolean mark) {
this.reference = reference;
this.mark = mark;
}
static <T> Pair<T> of(T reference, boolean mark) {
return new Pair<T>(reference, mark);
}
} private volatile Pair<V> pair;

看看它的cas操作:

 public boolean compareAndSet(V       expectedReference,
V newReference,
boolean expectedMark,
boolean newMark) {
Pair<V> current = pair;
return
expectedReference == current.reference && //在expectReference == current.Ref && expectMark == current.mark 并且新值pair中有任意一个或者两个都不等于目前值时,才更新
expectedMark == current.mark &&
((newReference == current.reference &&
newMark == current.mark) ||
casPair(current, Pair.of(newReference, newMark)));
}

AtomicStampedReference类维护带有整数”标志“的对象引用,可以用原子方法对其进行更新。对比AtomicMarkableReference类的pair<Object, Boolean>,AtomicStampedReference维护的是一种类似于<Object, Integer>的数据结构,其实是对对象引用的一个并发计数。

 private static class Pair<T> {
final T reference;
final int stamp;
private Pair(T reference, int stamp) {
this.reference = reference;
this.stamp = stamp;
}
static <T> Pair<T> of(T reference, int stamp) {
return new Pair<T>(reference, stamp);
}
} private volatile Pair<V> pair;
  public boolean compareAndSet(V   expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair;
return
expectedReference == current.reference &&
expectedStamp == current.stamp &&
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}

两种在解决CAS ”ABA“问题上很有用。

参考:http://www.blogjava.net/xylz/archive/2010/07/02/325079.html

J.U.C atomic 数组,字段原子操作的更多相关文章

  1. spring 怎样将枚举项注入到bean的数组字段中

    在配置文件的xmlns中引入util的scheam xmlns:util=http://www.springframework.org/schema/util 在配置文件的xmlns:util=&qu ...

  2. markModified声明要修改的数组字段

    更新一个文档的字段的时候,如果该字段的类型是数组类型,则必须在更新保存前声明一下这个数组字段要被修改,否则这个数组字段的值不会被修改.如 article.markModified('categorys ...

  3. 牛客多校第3场 J 思维+树状数组+二分

    牛客多校第3场 J 思维+树状数组+二分 传送门:https://ac.nowcoder.com/acm/contest/883/J 题意: 给你q个询问,和一个队列容量f 询问有两种操作: 0.访问 ...

  4. J.U.C Atomic(二)基本类型原子操作

    java.util.concurrent.atomic包中对基本类型进行原子操作的类有:AtomicInteger.AtomicBoolean.AtomicLong. 下面通过一个测试程序来验证一下A ...

  5. 多线程爬坑之路-J.U.C.atomic包下的AtomicInteger,AtomicLong等类的源码解析

    Atomic原子类:为基本类型的封装类Boolean,Integer,Long,对象引用等提供原子操作. 一.Atomic包下的所有类如下表: 类摘要 AtomicBoolean 可以用原子方式更新的 ...

  6. [Java多线程]-J.U.C.atomic包下的AtomicInteger,AtomicLong等类的源码解析

    Atomic原子类:为基本类型的封装类Boolean,Integer,Long,对象引用等提供原子操作. 一.Atomic包下的所有类如下表: 类摘要 AtomicBoolean 可以用原子方式更新的 ...

  7. J.U.C atomic AtomicInteger解析

    很多情况下我们只是需要简单的,高效,线程安全的递增递减方法.注意,这里有三个条件:简单,意味着程序员尽可能少的底层或者实现起来比较简单:高效,意味着耗用资源要少,程序处理速度要快: 线程安全也非常重要 ...

  8. J.U.C Atomic(一)CAS原理

    CAS概念 CAS:Compare And Swap,比较并交换.java.util.concurrent包完全是建立于CAS机制之上的. CAS原理 Java CAS是通过调用Unsafe的nati ...

  9. Mongo命令批量更新某一数组字段的顺序

      db.table.find().forEach(function (doc) {     var oldValue = doc.Column1;     var newValue = [sa[1] ...

随机推荐

  1. Life in Changsha 第一次scrum冲刺

    第一次冲刺任务 基于大局的全面性功能框架定位,要求能实现用户基于自己的需求进行的一系列操作. 用户故事 用户打开“生活在长大”的界面 程序首页展示校园服务,论坛等相关信息 用户选择某个功能 程序界面跳 ...

  2. 程序包管理rpm、yum与简单编译安装程序

    Linux程序包管理 Linux中软件的安装主要有两种形式:一种是直接下载源代码包自行编译后安装,另一种直接获取rpm软件包进行安装. 程序的组成部分: 二进制程序:程序的主体文件,比如我们运行一个l ...

  3. Java笔记:语法基础

    Java语法基础 更新时间:2018-1-7 10:34:05 Hello World 文件名:HelloWorld.java public class HelloWorld { public sta ...

  4. Mac Sublime text3 如何设置更加漂亮好用?

    说明:配置是根据自己的需求搜索了蛮多博客测试总结的. 显示效果 配置信息: command + , [逗号], 右侧配置信息 { "color_scheme": "Pac ...

  5. 搭建Vue.js开发环境(window10)

    我在配置Vue.js环境的时候遇到了很多的问题,希望能把这些解决方法也介绍给大家,希望能帮到大家,共同学习. 如果要转发,请注明原作者和原产地,谢谢! 注释:下面任何命令都是在windows的命令行工 ...

  6. python3基础(一)

    1. python文件主程序入口文件一般来要申明python路径,编码信息,作者说明等: #!/usr/bin/env python # _*_ coding: utf-8 _*_ # Author: ...

  7. 七牛php-sdk使用

    使用七牛云存储服务有一年多了,大部分功能基于其PHP-SDK来做开发,现对sdk的一些功能做一个总结. 一.资源上传 上传资源文件到七牛空间的不同实现方法 二.文档转换 介绍如何使用七牛以及七牛第三方 ...

  8. junit测试模板 unit-test

    一个项目能否发布上线,重要的环节就是测试.经过集成测试.性能测试.压力测试等不断循环的测试过后依据测试报告来确定上线.这些由专业的测试人员来完成,因此会导致程序开发者对自身的单元测试的弱化.若在代码中 ...

  9. 统计0到n之间1的个数[数学,动态规划dp](经典,详解)

    问题描述 给定一个十进制整数N,求出从1到N的所有整数中出现”1”的个数.  例如:N=2时 1,2出现了1个 “1” . N=12时 1,2,3,4,5,6,7,8,9,10,11,12.出现了5个 ...

  10. ex_gcd(个人模版)

    ex_gcd: #include<stdio.h> #include<string.h> using namespace std; int x,y; int ex_gcd(in ...