JUC: AtomicXXX

使用

JDK在 java.util.concurrent.atomic 下提供了一系列AtomicXX类型,这些类型提供了一系列原子方法,可以避免线程安全问题。下面是AtomicInteger的使用方式:

AtomicInteger i = new AtomicInteger(10);
i.incrementAndGet(); // 多个线程同时增加,不会造成线程安全问题

原理

Atomic类型皆是使用CAS(CompareAndSwap)来实现的。在java中,compareAndSwap也是一个方法,位于UnSafe类中:

// AtomicInteger#incrementAndGet
public final int incrementAndGet() {
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1; // 给对象this的valueOffset属性+1
}
// UnSafe#getAndAddInt
    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)); // 如果不是期望值(var5仍是当前值,说明没有被其他线程修改),返回false,如果是期望值则设置为 var5+1 目标值

        return var5;
    }

CAS需要CPU原语支持,也称为无锁优化,自旋锁。

ABA问题

使用CAS进行无锁优化时,有可能引发ABA问题:

变量A不见得是数值,也有可能是对象。对象引用虽然不变,但是成员变量可能已经发生变化。

通俗点说就是,你的前女友在和你复合之后,看似和以前没有区别,但是其实已经经历了n个男人,这就是ABA问题。ABA问题针对简单数字增减一般是不会出现问题,是允许的;但是针对其他对象操作,就有可能发生问题。

解决ABA问题

解决ABA问题最容易想到的办法就是给变量增加版本号,JDK已经了类 AtomicStampedReference 类解决ABA问题:

LongAdder

此外,在 java.util.concurrent.atomic 下还包含XXXAdder类,以LongAdder为例:

LongAdder采用分段锁,这使得LongAdder的效率高于synchronized、Atomic。

最后更新于

这有帮助吗?