来谈谈 java.util.concurrent.atomic 并发包中原子类(AtomicXxxx JDK1.8 源码分析)

Atomic(原子相关类)# 系列文章目录

为了保证对变量的正确修改我们使用JUC并发包中的原子类



并发包(原子类)

在这里插入图片描述


提示:以下是本篇文章正文内容,下面案例可供参考

一、基本类型原子类

  1. AtomicLong:long类型
  2. AtomicInteger:int类型
  3. AtomicBoolean:布尔类型

AtomicInteger源码分析

构造器

//无参构造时成员变量value默认是0
public AtomicInteger() {
    
    
}

//指定value初始值
public AtomicInteger(int initialValue) {
    
    
    value = initialValue;
}

成员变量

private volatile int value; //实际存值的变量,volatile修饰内存保证可见性

private static final long valueOffset;// value在对象的偏移量

private static final Unsafe unsafe = Unsafe.getUnsafe();//魔法类,原子操作都依赖其实现

静态初始化块

static {
    
    
    try {
    
    
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value")); //计算value偏移量
    } catch (Exception ex) {
    
     throw new Error(ex); }
}

主要方法

//获取成员变量的值(读操作是线程安全的)
public final int get() {
    
     return value;}

//设置成员变量的值(直接设置值由于不依赖与先前的值所以无需cas)
public final void set(int newValue) {
    
     value = newValue;}

//自增操作通过unsafe实现安全自增,返回自增后的结果
public final int incrementAndGet() {
    
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1;}

//自减同上
public final int decrementAndGet() {
    
    return unsafe.getAndAddInt(this, valueOffset, -1) - 1;}

//与上类似(返回旧值)
public final int getAndIncrement() {
    
    return unsafe.getAndAddInt(this, valueOffset, 1);}

//与上类似(返回旧值)
public final int getAndDecrement() {
    
    return unsafe.getAndAddInt(this, valueOffset, -1);}

//通过unsafe安全的增加delta(返回旧值)
public final int getAndAdd(int delta) return unsafe.getAndAddInt(this, valueOffset, delta);}

//通过unsafe安全的设置newValue(返回旧值)
public final int getAndSet(int newValue) {
    
    return unsafe.getAndSetInt(this, valueOffset, newValue);}

//强制转换相关
public float floatValue() {
    
    return (float)get();}
public double doubleValue() {
    
    return (double)get();}
public long longValue() {
    
    return (long)get();}

小结 :由上可以我们通过volatile和Unsafe来保证变量原子性的特征

二、数组类型原子类

  1. AtomicLongArray:long型数组
  2. AtomicIntegerArray:int型数组
  3. AtomicReferenceArray:引用类型数组

AtomicIntegerArray源码分析

构造方法

//指定一个数组长度来构造
public AtomicIntegerArray(int length) {
    
    
    array = new int[length];
}

//直接传入一个数组来构造
public AtomicIntegerArray(int[] array) {
    
    
    // Visibility guaranteed by final field guarantees
    this.array = array.clone();
}

成员变量

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;//实际存数据的数组

静态初始化块

static {
    
    
    int scale = unsafe.arrayIndexScale(int[].class); //返回数组中元素大小(此处int为4)
    if ((scale & (scale - 1)) != 0) //scale必须为2的次幂
        throw new Error("data type scale not a power of two");
// numberOfLeadingZeros计算scale二进制高位0的个数此处为29(遇到第一个不为0 停止)
//shift表示数组元素大小2的次幂的指数这里int为4 = 2^2 所以shift为2
//之后计算数组偏移时直接使用base + (i << shift)
    shift = 31 - Integer.numberOfLeadingZeros(scale); 
}

主要方法

//保证可见性的方式获取数组中的元素
public final int get(int i) {
    
    
    return getRaw(checkedByteOffset(i));
}

//数组边界检查
private long checkedByteOffset(int i) {
    
    
    if (i < 0 || i >= array.length)
        throw new IndexOutOfBoundsException("index " + i);

    return byteOffset(i); //直接返回数组中下标为i的变量偏移
}

//计算数组中元素的偏移
private static long byteOffset(int i) {
    
    
    return ((long) i << shift) + base; //基址 + 偏移
}

//根据偏移直接从数组中获取元素
private int getRaw(long offset) {
    
    
    return unsafe.getIntVolatile(array, offset);
}

//保证内存可见的设置下标位置为i的元素
public final void set(int i, int newValue) {
    
    
    unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
}

//保证内存可见的设置下标位置为i的元素,并返回旧值
public final int getAndSet(int i, int newValue) {
    
    
    return unsafe.getAndSetInt(array, checkedByteOffset(i), newValue);
}

//通过CAS改变下标为i位置的元素,成功返回ture
public final boolean compareAndSet(int i, int expect, int update) {
    
    
    return compareAndSetRaw(checkedByteOffset(i), expect, update);
}

//原子的加1
 public final int getAndIncrement(int i) {
    
    
    return getAndAdd(i, 1);
}

//原子的减1
public final int getAndDecrement(int i) {
    
    
    return getAndAdd(i, -1);
}

//设置新值(其他线程在一小段时间内还是会返回旧值)
public final void lazySet(int i, int newValue) {
    
    
    unsafe.putOrderedInt(array, checkedByteOffset(i), newValue);
}


小结 :AtomicIntegerArray保证了对数组中每个元素的操作都是安全的。

三、引用类型原子类

  1. AtomicReference:与AtomicInteger类似,该类保证对象的修改的安全性
  2. AtomicStampedReference
  3. AtomicMarkableReference

AtomicStampedReference源码分析

CAS存在ABA问题:CAS是通过比较数据是否为期望值,其目的是判断数据是否发生改变,但是当数据从A变到B又变到A时实际上数据发生了改变但是CAS认为其没有发生改变。

构造方法

//传入一个需要保护的对象和一个版本号
public AtomicStampedReference(V initialRef, int initialStamp) {
    
    
    pair = Pair.of(initialRef, initialStamp);
}

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);
    }
}

可见AtomicStampedReference中的对象实际存在于静态内部类中,还需要传入一个版本号,来记录对象的修改。

成员变量

private volatile Pair<V> pair; //实际存对象的静态内部类
private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
private static final long pairOffset =
    objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class); //pair内存偏移

主要方法

public V getReference() {
    
     //获取对象
    return pair.reference;
}

public int getStamp() {
    
     //获取当前对象版本号
    return pair.stamp;
}

//如果预取对象和版本号 和 当前的相同 就CAS修改pair 为 新的对象和版本号成功返回ture
//如果预取对象和版本号 和 当前的不相 直接返回false
public boolean weakCompareAndSet(V  expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp) {
    
    
    return compareAndSet(expectedReference, newReference,
                         expectedStamp, newStamp);
}

public boolean compareAndSet(V  expectedReference,
                             V   newReference,
                             int expectedStamp,
                             int newStamp) {
    
    
    Pair<V> current = pair;//当前pair(对象+版本号)
    return
        expectedReference == current.reference &&
        expectedStamp == current.stamp && //判断期望对象和版本号和当前的是否相同
        ((newReference == current.reference &&
          newStamp == current.stamp) || //判断新对象和版本号是否和当前的相同
         casPair(current, Pair.of(newReference, newStamp))); //创建一个新的pair
}

//CAS改变pair变量,成功返回true
private boolean casPair(Pair<V> cmp, Pair<V> val) {
    
    
    return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}

//设置新的对象和版本号(如果和当前版本号和对象都相不修改)
public void set(V newReference, int newStamp) {
    
    
    Pair<V> current = pair;
    if (newReference != current.reference || newStamp != current.stamp)
        this.pair = Pair.of(newReference, newStamp);
}

//尝试修改版本号不修改对象(如果对象已被修改则版本号修改失败)
public boolean attemptStamp(V expectedReference, int newStamp) {
    
    
    Pair<V> current = pair; //当前pair
    return
        expectedReference == current.reference && //对象期望值和当前值是否相同
        (newStamp == current.stamp || //版本号已经是想修改的值
         casPair(current, Pair.of(expectedReference, newStamp)));
}

小结:AtomicStampedReference引入版本号解决了CAS中存在的ABA问题。AtomicMarkableReference与AtomicStampedReference类似,不过版本只有两种状态即ture和false。

四、对象属性修改原子类

  1. abstract AtomicLongFieldUpdater
  2. abstract AtomicIntegerFieldUpdater
  3. abstract AtomicReferenceFieldUpdater

AtomicIntegerFieldUpdater源码分析

构造方法

protected AtomicIntegerFieldUpdater() {
    
     //抽象类是无法被直接构造的
}

//AtomicIntegerFieldUpdater通过静态方法创建(创建的实际上是其内部类)
@CallerSensitive
public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,
                                                          String fieldName) {
    
    
    return new AtomicIntegerFieldUpdaterImpl<U>
        (tclass, fieldName, Reflection.getCallerClass());
}

// AtomicIntegerFieldUpdaterImpl构造方法
AtomicIntegerFieldUpdaterImpl(final Class<T> tclass, //需要升级的类
                              final String fieldName,//需要升级类中变量名
                              final Class<?> caller) {
    
    
    ……

    if (field.getType() != int.class) //升级的变量必须为intl类型
        throw new IllegalArgumentException("Must be integer type");

    if (!Modifier.isVolatile(modifiers)) //升级的变量必须是volatile类型
        throw new IllegalArgumentException("Must be volatile type");

    this.cclass = (Modifier.isProtected(modifiers) &&
                   tclass.isAssignableFrom(caller) &&
                   !isSamePackage(tclass, caller))
                  ? caller : tclass;
    this.tclass = tclass; //需要升级类的class
    this.offset = U.objectFieldOffset(field);//变量在该类中的偏移
}

// AtomicIntegerFieldUpdaterImpl成员方法
private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();//unsafe
private final long offset; //变量偏移
private final Class<?> cclass;
private final Class<T> tclass; //持有字段的类


其他一些方法(实现了AtomicIntegerFieldUpdater)

在这里插入图片描述

小结:AtomicIntegerFieldUpdater是用于类中变量的修改的安全性,对变量的升级,变量必须是volatile并且是int类型的。我们可以通过对类中变量进行升级然后通过该类来安全的操作变量,对变量进行修改。AtomicInteger也可以为什么还要它,AtomicInteger创建本身需要一定的性能,有时候我们可能只需要偶尔的对变量进行原子操作,此时AtomicIntegerFieldUpdater是不错的选择。如果的列子来理解AtomicIntegerFieldUpdater的好处。

例子

public class Test implements Runnable {
    
    

    private volatile int num; //变量必须是int 和 volatile

    private static Test unsafeClass = new Test();
    private static Test safeClass   = new Test();

    //变量升级
    AtomicIntegerFieldUpdater<Test> up = AtomicIntegerFieldUpdater.newUpdater(Test.class,"num");

    public static void main(String[] args) throws Exception {
    
    
        new Thread(unsafeClass).start();
        new Thread(safeClass).start();

        TimeUnit.SECONDS.sleep(2);

        System.out.println("unsafeClass: " + unsafeClass.num);
        System.out.println("safeClass: " + safeClass.num);
        
    }

    @Override
    public void run() {
    
    
        for(int i = 0 ; i < 10000 ; i++)
        {
    
    
            unsafeClass.num++; //不安全增加
            up.incrementAndGet(safeClass); //安全增加(将safeClass包装起来)
        }
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_41237676/article/details/108984695