jdk1.8 AtomicInteger以及AtomicStampedReference源码分析

AtomicInteger

AtomicInteger可以保证在多线程环境下正确地增减
底层使用的是cas,volatile,unsafe来实现的

重要属性

// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;

static {
    try {
    	// 通过unsafe获取指定属性的偏移量
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
    } catch (Exception ex) { throw new Error(ex); }
}
// 将value声明为voliatile可以保证可见性,但是不能保证原子性
// 即一个线程成功修改了volatile修饰的变量的值,会令其他线程的工作内存中该变量的副本失效,使得其他线程再去读取这个变量值时是从内存中读取最新的值
// 如果不使用volatile修饰,那么一个线程修改了变量的值,只修改了工作内存中的值,并没有修改主内存中的值,因此其他变量读取时仍然读取的是工作内存中的旧的值
private volatile int value;

构造函数

构造函数十分简单,就是简单的赋值

public AtomicInteger(int initialValue) {
    value = initialValue;
}

修改操作

public final int addAndGet(int delta) {
	// this代表当前AtomicInteger的地址,valuesOffset代表value属性的偏移量
	// 使用cas进行更新
    return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}
public final int getAndAddInt(Object var1, long var2, int var4) {
 int var5;
 do {
     var5 = this.getIntVolatile(var1, var2);
 } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

 return var5;
}

AtomicStampedReference

AtomicStampedReference主要使用来解决ABA问题的
所谓ABA问题指的就是在A线程进行cas操作时,首先读取了变量的值为0,然后时间片用完,线程B通过cas成功将变量从0更改为1,然后又通过cas将变量从1更改为0,线程B时间片用完,线程A获得时间片,此时再读取变量的值仍然为0,那么线程A就判断变量没有改变,即没有线程对该变量操作,接着线程A就修改变量的值
解决方法就是另外使用一个版本号,在判断变量是否修改时,不光要判断变量的值是否改变,还需要判断版本号是否改变,只有这两个都没有改变,我们才任务变量没有被其他线程改变

属性

AtomicStampedReference底层最重要的属性就是一个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 boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp) {
    Pair<V> current = pair;
    // 当前引用和期望的引用相同,版本号也一致
    // 代表没有其他线程修改变量
    // 接下来尝试set
   	// 如果新的引用和现有引用相同并且新的版本号和现有版本号也相同,那么就不需要再set了
   	// 如果不同,那么就尝试使用cas修改内存中的变量值
    return
        expectedReference == current.reference &&
        expectedStamp == current.stamp &&
        ((newReference == current.reference &&
          newStamp == current.stamp) ||
         casPair(current, Pair.of(newReference, newStamp)));
}
```java
 private boolean casPair(Pair<V> cmp, Pair<V> val) {
    return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}

猜你喜欢

转载自blog.csdn.net/lxlneversettle/article/details/88814173
今日推荐