Java多线程——无锁思想/源码解读:Atomic

原子类型

原子类型在java.util.concurrent.atomic包下 有四种类型(每种各3个类):

基本类型

AtomicInteger:原子操作整数类型
AtomicBoolean:基本同上,操作布尔类型
AtomicLong:基本同上,操作Long类型

引用类型

AtomicReference:引用普通的对象,在并发场景下修改对象时保证线程安全
AtomicStampedReference:在AtomicReference基础上增加有版本号.
AtomicMarkableReference:在AtomicReference基础上增加有标记位.

数组类型

AtomicIntegerArray
AtomicLongArray
AtomicReferenceArray

更新属性类型:

AtomicIntegerFieldUpdater
AtomicLongFieldUpdater
AtomicReferenceFieldUpdater

CAS

原理

CAS(V,E,N): V表示操作对象的值,E表示预期值,N表示新值

当要执行更新操作时,会先在同步方法中比较V和E是否相等,如果相等说明V没有被其他线程所修改,这时才会用N替换V,否则什么也不做.

sun.misc.Unsafe类:

该类提供了绕开JVM的功能,如实例化只有私有构造方法的类,通过内存偏移地址修改变量值,创建容量大于Integer.MAX_VALUE的数组等.是CAS实现的核心.

主要提供了一下3种CAS方法:

public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object x);
public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);
public final native boolean compareAndSwapLong(Object o, long offset, long expected, long x);

AtomicInteger

i++和++i是非线程安全的,想要在多线程环境下使用,很自然的就想到了使用synchronized,

实际上AtomicInteger也是线程安全的,但和synchronized的理念不同,是基于CAS原理实现的.

结构

private static final Unsafe unsafe = Unsafe.getUnsafe();
// value内存地址
private static final long valueOffset;
// 存储值的变量
private volatile int value;

以incrementAndGet()方法为例:

Java7中:

public final int incrementAndGet() {
    for (;;) {// CAS实现的关键
        int current = get();// 获得当前值
        int next = current + 1;// 增
        if (compareAndSet(current, next))// 传入预期值和更新值
            return next;
    }
}
// 抽出compareAndSet()方法
public final boolean compareAndSet(int expect, int update) {
    // 更新时,会将expect和现在内存中的值比较,相同才会用update覆盖内存中的值
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

Java8中:

public final int incrementAndGet() {
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
// Unsafe类中的getAndAddInt(),delta:预期增加的量
public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    do {
        v = getIntVolatile(o, offset);// native方法
    } while (!compareAndSwapInt(o, offset, v, v + delta));
    return v;
}

JDK中似乎没有Unsafe的源码,可参看OpenJDK-Unsafe

可以看出,无论是7还是8中,最终都是使用了Unsafe类的compareAndSwapInt()进行预期值和新值的比较.

AtomicReference

AtomicReference

AtomicReference<String> atomicReference = new AtomicReference<String>();
atomicReference.set("newValue");
atomicReference.compareAndSet("newValue", "updateUser");
// atomicReference.getAndSet("newValue", "updateUser");

结构上和AtomicInteger不同的就是存的对象不再是基本类型,常用的方法也都是类似的,但存在ABA问题.

ABA问题

ABA问题:线程1准备修改变量,获取到值为A,此时线程2将其修改成B,然后又修改回A, 对于线程1来说变量并没有发生变化,但实际上发生了变化,对于无状态的数据来说没有特殊的影响,但对于引用类型就不是这样了,尤其是针对链式结构.

AtomicStampedReference

private static class Pair<T> {
    final T reference;// 存储用变量
    final int stamp;// 版本号
}
private volatile Pair<V> pair;// 存储用变量(容器)
// 构造方法
public AtomicStampedReference(V initialRef, int initialStamp) {
    pair = Pair.of(initialRef, initialStamp);
}

只有一个构造方法,实例化时就要指定版本号,创建一个AtomicStampedReference对象,实质就是创建了一个Pair对象. 由于版本号的存在,一定程度上能够解决ABA问题.

其compareAndSet更新操作如下:

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))); // 对象和版本号有变更的更新
}
// 上面的casPair
private boolean casPair(Pair<V> cmp, Pair<V> val) {
    return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}

值得注意的是,此处使用的是&&逻辑符,当其前面出现false的结果时,后面的内容就不再执行, 因此,预期值和内存值不一致,后面的casPair就不会执行,上面的return语句很好的符合CAS原理.

AtomicMarkableReference和AtomicStampedReference基本相同,只不过提供的不是版本号,而是一个标志位.

Atomic Array

AtomicIntegerArray

int[] value = new int[] { 1, 2, 3, 4, 5 };
AtomicIntegerArray a3 = new AtomicIntegerArray(value);
a3.getAndSet(2, 100);

内部的元素也都可以使用AtomicInteger的操作.

数组元素类型也可以是应用类型,此时就可以使用AtomicReferenceArray类.

Atomic FieldUpdater

都是抽象类,可以通过newUpdater()方法获取其对象, AtomicIntegerFieldUpdater针对的是引用对象的普通类型属性,AtomicReferenceFieldUpdater针对的是引用对象的引用类型属性.

使用例:

AtomicIntegerFieldUpdater a4 = AtomicIntegerFieldUpdater.newUpdater(User.class, "age");
User user = new User(0, null);
a4.getAndIncrement(user);
 
AtomicReferenceFieldUpdater a5 = AtomicReferenceFieldUpdater.newUpdater(User.class, User.class, "next");
User user1 = new User(2, user);
User user2 = new User(3, user1);
a5.getAndSet(user2, user);
 
static class User {
    public volatile int age = 8;
    public volatile User next;
 
    User(int age, User next) {
        this.age = age;
        this.next = next;
    };
}

你可能感兴趣的:(JAVA多线程,JAVA锁,java,开发语言,后端,无锁,Atomic)