【并发包】Atomic相关

2019-05-03  本文已影响0人  良辰夜

1. 统一API一览

注意:

  1. oldValue指方法内部在执行操作前,通过get方法获取得值,而newValue则是已经执行操作后的值
  2. 一般get在前面,说明返回的是旧值否则为新值。
方法 参数 返回 详细解释
set newValue void validate式设置
lazySet newValue void 赋值式设置(其他线程不一定可见)
getAndSet newValue oldValue validate式设置
compareAndSet expect,update boolean 比较expect,如相同则设置并返回true,如不同则不设置并返回false
weakCompareAndSet expect,update boolean
getAndIncrement void oldValue 原子++
getAndDecrement void oldValue 原子--
getAndAdd delta oldValue 原子+delta
incrementAndGet void newValue 原子++
decrementAndGet void newValue 原子--
addAndGet delta newValue 原子+delta
getAndUpdate xxxUnaryOperator (FunctionalInterface ,一个参数一个返回结果) oldValue 把oldValue给xxxUnaryOperator
updateAndGet xxxUnaryOperator (FunctionalInterface ,一个参数一个返回结果) newValue 把oldValue给xxxUnaryOperator得到返回值并返回
getAndAccumulate x,xxxBinaryOperator oldValue 把x和oldValue给xxxBinaryOperator
accumulateAndGet x,xxxBinaryOperator newValue 把x和oldValue给xxxBinaryOperator得到返回值并返回

2 AtomicInt (细讲)

2.1一些变量

unsafe :每个atomic类基本都必须使用这个类。
valueOffset:value这个变量在atomicInt这个类的内存上的偏移值。
value:当前对象的值。

private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;
    static {
        try {
            //通过字节码,拿到value字段相较于整个atomic对象的偏移值。
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
    private volatile int value;

2.2 set/lazySet

//更新值时,因为value是volatile,所以其他线程立刻可见。
public final void set(int newValue) {
    value = newValue;
}

// 更新值时,不保证其他线程可见。相对于set应该有性能上优势。
public final void lazySet(int newValue) {
    unsafe.putOrderedInt(this, valueOffset, newValue);
}

2.3 getAndSet

public final int getAndSet(int newValue) {//返回上一次的值,并set新的value
    return unsafe.getAndSetInt(this, valueOffset, newValue);
}

public final int getAndSetInt(Object o, long offset, int newValue) {
    int v;
    do {
        v = getIntVolatile(o, offset);//通过偏移量拿到value
    } while (!compareAndSwapInt(o, offset, v, newValue));//如果这次value和上次value不同,那么返回false
    return v;
}

2.4 compareAndSet、weakCompareAndSet

在java9之前,这个两个方法基本一样,我们尽量用compareAndSet。

//比较expect是否与value值一直,如果不一致,就返回false,并不更新。
public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
public final boolean weakCompareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

2.5 unsafe.getAndAddInt 相关的方法

先看 getAndAddInt 方法源码

//可以看出这个方法,就是原子性对value加上一个delta,并返回原值
public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    do {
        v = getIntVolatile(o, offset);//通过偏移量拿到值
    } while (!compareAndSwapInt(o, offset, v, v + delta));//原子性对值+delta。
    return v;
}

在看atomic相关方法:

//对value+1并返回原值
public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}
//对value-1并返回原值
public final int getAndDecrement() {
    return unsafe.getAndAddInt(this, valueOffset, -1);
}
//对value加上一个delta并返回原值
public final int getAndAdd(int delta) {
    return unsafe.getAndAddInt(this, valueOffset, delta);
}
//对value+1并返回新值
public final int incrementAndGet() {
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
//对value-1并返回新值
public final int decrementAndGet() {
    return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
}
//对value加上一个delta并返回新值
public final int addAndGet(int delta) {
    return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}

2.6 getAndUpdate、updateAndGet、getAndAccumulate、accumulateAndGet

//将旧值传给updateFunction得到新值,然后比较更新,如失败则重复循环,直至成功返回旧值
public final int getAndUpdate(IntUnaryOperator updateFunction) {
    int prev, next;
    do {
        prev = get();
        next = updateFunction.applyAsInt(prev);
    } while (!compareAndSet(prev, next));
    return prev;
}
//将旧值传给updateFunction得到新值,然后比较更新,如失败则重复循环,直至成功返回新值
public final int updateAndGet(IntUnaryOperator updateFunction) {
    int prev, next;
    do {
        prev = get();
        next = updateFunction.applyAsInt(prev);
    } while (!compareAndSet(prev, next));
    return next;
}
//将旧值和x传给accumulatorFunction得到新值,然后比较更新,如失败则重复循环,直至成功返回旧值
public final int getAndAccumulate(int x,
                                  IntBinaryOperator accumulatorFunction) {
    int prev, next;
    do {
        prev = get();
        next = accumulatorFunction.applyAsInt(prev, x);
    } while (!compareAndSet(prev, next));
    return prev;
}
//将旧值和x传给accumulatorFunction得到新值,然后比较更新,如失败则重复循环,直至成功返回新值
public final int accumulateAndGet(int x,
                                  IntBinaryOperator accumulatorFunction) {
    int prev, next;
    do {
        prev = get();
        next = accumulatorFunction.applyAsInt(prev, x);
    } while (!compareAndSet(prev, next));
    return next;
}

2.7 总结 AtomicInt

1.如果前面是getxxx,那么更新后返回旧值,否则就返回更新后的新值。
2.核心方法,都是依靠compareAndSet

3. AtomicBoolean

value是通过int来实现,几乎和AtomicInteger一模一样,其中0表示false,而1表示true。

private volatile int value;
public AtomicBoolean(boolean initialValue) {
    value = initialValue ? 1 : 0;
}

4. AtomicLong

由于AtomicLong方法基本和前面AtomicInt一致所以不细写Api分析了。
仅仅写下唯一差别。

static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
private static native boolean VMSupportsCS8();

这些事给jvm来调用的。
有些cpu是32位的数据总线一次就只能传32个字节,long是64个字节,可能需要传两次,这样就不能保持原子性。所以需要加锁。

如果JVM的long操作是原子化的,会采用无锁的CAS来更新,如果不支持就会使用带锁的方式来更新。

所以尽量使用int类型。如果一定要用long类型,尽量保证cpu是64位的,系统是64位的。

5. AtomicReference

和前面的不同,AtomicReference 提供了对Obeject类型的compareAndSet,当然compare的是java引用

API和前面的atomicInteger基本一模一样(当然原子++--都没有了)。

6. ABA问题和AtomicStampedReference

6.1 ABA问题引入。

线程要修改变量值:
线程t1: a->c
线程t2: a->b->a

t1先拿到变量值为a,然后t2执行a->b->a,此时线程t1 进行CAS发现变量值还是a,所以将a->c。
对比sychronized,如果使用sychronized,线程t2就无法对变量进行修改。
而使用CAS则会导致,线程t1仅仅只能比较变量值是否相等,而无法知道之前是否已经发生变化了。

这其实和挪用资产挺像的,比如小张挪用100W公司资产炒股,赚了10W,让后把100W补回公司,这样公司查账后发现资产没变化,但是小张多了10W

6.2 AtomicStampedReference

可以看出来CAS其实就好像乐观锁一样,因此我们可以参考一下SVN的解决方案加上version。

而AtomicStampedReference中的Stamped就是类似version的做法。

6.2.1 Pair

Pair可以说是唯一个和AtomicReference 类的区别。
Pair的作用就是给value,也就是给 T refresh绑定了一个时间搓。返回一个新类叫pair。

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 AtomicStampedReference(V initialRef, int initialStamp) {
    pair = Pair.of(initialRef, initialStamp);
}
6.2.2 compareAndSet

就写法而言还是有点炫酷,类似js的写法,如果是A&&B那么A为true会执行B,如果是A||B,那么A为false则会执行B。

  1. 这里先比较传进来的expectedReference和expectedStamp是否和当前pair一致,如果不一致返回false。
  2. 再比较传进来的newReference和newStamp是否和pair一一致,如果一一致则直接返回true。
  3. 如果不一致,则进行CAS后返回。
public boolean compareAndSet(V   expectedReference,
                             V   newReference,
                             int expectedStamp,
                             int newStamp) {
    Pair<V> current = pair;
    return
        expectedReference == current.reference && expectedStamp == current.stamp //1  
        && (
                (newReference == current.reference && newStamp == current.stamp) //2
                || casPair(current, Pair.of(newReference, newStamp))//3
        );
}

7.AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray

作用:对数组内部的元素进行原子操作。

我们以AtomicIntegerArray为例:

7.1 int数组内存中结构

可以看出主要由两部分构成:header ,int 元素
其中每个int元素为4个字节,而header则是固定的。


int 数组在内存中的结构

7.2 分析AtomicIntegerArray核心代码

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);//拿到每个元素占用的字节
    if ((scale & (scale - 1)) != 0)//判断是否为2的幕
        throw new Error("data type scale not a power of two");
    shift = 31 - Integer.numberOfLeadingZeros(scale);//相当于Integer.numberOfTrailingZeros。
}

private long checkedByteOffset(int i) {
    if (i < 0 || i >= array.length)
        throw new IndexOutOfBoundsException("index " + i);

    return byteOffset(i);
}

private static long byteOffset(int i) {
    return ((long) i << shift) + base;//i<<shift是指 i前面占用的offset
}
上一篇下一篇

猜你喜欢

热点阅读