并发类 AtomicInteger 使用入门及源码详解

AtomicInterger 介绍

能够原子性更新的 Integer 值,固然这个类并不能彻底替代 Integer 对象。html

AtomicInterger

使用

使用起来仍是很方便的。java

好比说咱们定义一个计数器,使用 AtomicInteger 能够同时兼顾性能与并发安全。程序员

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author binbin.hou
 * @since 1.0.0
 */
public class Counter {

    private AtomicInteger c = new AtomicInteger(0);

    /**
     * 递增
     */
    public void increment() {
        c.getAndIncrement();
    }

    /**
     * 获取值
     * @return 值
     */
    public int value() {
        return c.get();
    }

    public static void main(String[] args) throws InterruptedException {
        final Counter counter = new Counter();

        //1000 threads
        for(int i = 0; i < 100 ; i++) {
            new Thread(new Runnable() {
                public void run() {
                    counter.increment();
                }
            }).start();
        }
        Thread.sleep(1000);
        System.out.println("Final number (should be 100): " + counter.value());
    }

}

日志输出:缓存

Final number (should be 100): 100

AtomicInteger 源码

可恶!这个类使用起来居然这么方便。安全

那么李大狗是如何实现的呢?并发

AtomicInteger 源码

类定义

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;
}

继承自 Number 类,实现了序列化接口。app

内部属性

这里初始化了 unsafe 变量,用于后面使用 CAS 作变量更新。jvm

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

// 值的偏移量
private static final long valueOffset;

static {
    try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
    } catch (Exception ex) { throw new Error(ex); }
}

// 定义的变量值
private volatile int value;
  • objectFieldOffset 方法

这是一个 native 方法,换言之就是直接调用的操做系统,获取到 value 变量的内存偏移量信息。ide

public native long objectFieldOffset(Field var1);

构造器

平淡无奇的构造器,用来初始化 value。性能

固然也能够不指定,不指定的时候默认值是什么呢?

我想各位读者确定都清楚,不清楚的能够留言区忏悔一下。

/**
 * Creates a new AtomicInteger with the given initial value.
 *
 * @param initialValue the initial value
 */
public AtomicInteger(int initialValue) {
    value = initialValue;
}

/**
 * Creates a new AtomicInteger with initial value {@code 0}.
 */
public AtomicInteger() {
}

基本的方法

/**
 * Gets the current value.
 *
 * @return the current value
 */
public final int get() {
    return value;
}

/**
 * Sets to the given value.
 *
 * @param newValue the new value
 */
public final void set(int newValue) {
    value = newValue;
}

/**
 * Returns the String representation of the current value.
 * @return the String representation of the current value
 */
public String toString() {
    return Integer.toString(get());
}
/**
 * Returns the value of this {@code AtomicInteger} as an {@code int}.
 */
public int intValue() {
    return get();
}
/**
 * Returns the value of this {@code AtomicInteger} as a {@code long}
 * after a widening primitive conversion.
 * @jls 5.1.2 Widening Primitive Conversions
 */
public long longValue() {
    return (long)get();
}
/**
 * Returns the value of this {@code AtomicInteger} as a {@code float}
 * after a widening primitive conversion.
 * @jls 5.1.2 Widening Primitive Conversions
 */
public float floatValue() {
    return (float)get();
}
/**
 * Returns the value of this {@code AtomicInteger} as a {@code double}
 * after a widening primitive conversion.
 * @jls 5.1.2 Widening Primitive Conversions
 */
public double doubleValue() {
    return (double)get();
}

这两个方法和普通类中的 getter/setter等并无区别,此处不作过多解释。

基于 unsafe 的方法

为何 AtomicInteger 能保持原子性呢?

咱们一块儿来看一下是如何基于 Unsafe 实现原子性的?

lazySet 惰性设置

/**
 * Eventually sets to the given value.
 *
 * @param newValue the new value
 * @since 1.6
 */
public final void lazySet(int newValue) {
    unsafe.putOrderedInt(this, valueOffset, newValue);
}

最终会把值设置为给定的值。这是什么意思?我直接懵了。

其实这个是相对的,咱们前面说过,volatile 修饰的变量,修改后能够保证线程间的可见性。可是这个方法,修改后并不保证线程间的可见性

这和之前在网上看到的可不同,不是说好的 AtomicXXX 都是基于 volatile+cas 实现的吗?这里为何要反其道而行之呢?

实际上是为了性能,lazySet 有本身的应用场景。

高级程序员都知道 volatile 能够保证变量在线程间的可见性,可是这里再问一句,不使用 volatile 修饰就没法保证可见性了吗?

事实上,这里彻底能够不用 volatile 变量来修饰这些共享状态,

  1. 由于访问共享状态以前先要得到锁, Lock.lock()方法可以得到锁,而得到锁的操做和volatile变量的读操做同样,会强制使CPU缓存失效,强制从内存读取变量。

  2. Lock.unlock()方法释放锁时,和写volatile变量同样,会强制刷新CPU写缓冲区,把缓存数据写到主内存

底层也是经过加内存屏障实现的。

而lazySet()优化原理,就是在不须要让共享变量的修改马上让其余线程可见的时候,以设置普通变量的方式来修改共享状态,能够减小没必要要的内存屏障,从而提升程序执行的效率。

这个讨论能够参考 stackoverflow 的问题 AtomicInteger lazySet vs. set

原子性设置值

/**
 * Atomically sets to the given value and returns the old value.
 *
 * @param newValue the new value
 * @return the previous value
 */
public final int getAndSet(int newValue) {
    return unsafe.getAndSetInt(this, valueOffset, newValue);
}

这个方法实现以下:

public final int getAndSetInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var4));
    return var5;
}

实际上就是咱们常说的 volatile + CAS 实现。

compareAndSet 比较而且设置

jdk 将 CAS 这个方法暴露给了开发者,不过作了一层封装,让 unsafe 类对使用者不可见。

compareAndSwapInt 这个方法是一个 native 方法,此处不作深刻。其余的方法不少都大同小异,因此咱们再也不赘述。

ps: 很烦,native 方法直接看源码就会变得很麻烦,之后有时间研究下 openJdk 之类的。

/**
 * Atomically sets the value to the given updated value
 * if the current value {@code ==} the expected value.
 *
 * @param expect the expected value
 * @param update the new value
 * @return {@code true} if successful. False return indicates that
 * the actual value was not equal to the expected value.
 */
public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

weakCompareAndSet

这个方法我以为也颇有趣,弱比较?拿泥搜来。

/**
 * Atomically sets the value to the given updated value
 * if the current value {@code ==} the expected value.
 *
 * <p><a href="package-summary.html#weakCompareAndSet">May fail
 * spuriously and does not provide ordering guarantees</a>, so is
 * only rarely an appropriate alternative to {@code compareAndSet}.
 *
 * @param expect the expected value
 * @param update the new value
 * @return {@code true} if successful
 */
public final boolean weakCompareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

咱们发现这两个方法在 jdk1.8 中其实是没有差别的。

底层调用的都是同一个方法:

public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

那区别是什么呢?

因而我就去查了一下,JDK1.9之前,二者底层实现是同样的,并无严格区分。

JDK 1.9提供了Variable Handles的API,主要是用来取代java.util.concurrent.atomic包以及sun.misc.Unsafe类的功能。

Variable Handles须要依赖jvm的加强及编译器的协助,即须要依赖java语言规范及jvm规范的升级。

VarHandle中compareAndSet和compareAndSet的定义以下:

(1)compareAndSet(Object... args)

Atomically sets the value of a variable to the newValue with the memory semantics of set(java.lang.Object...) if the variable's current value, referred to as the witness value, == the expectedValue, as accessed with the memory semantics of getAcquire(java.lang.Object...).

(2)weakCompareAndSet(Object... args)

Possibly atomically sets the value of a variable to the newValue with the memory semantics of setVolatile(java.lang.Object...) if the variable's current value, referred to as the witness value, == the expectedValue, as accessed with the memory semantics of getVolatile(java.lang.Object...).

weakCompareAndSet的描述多了一个单词Possibly,可能的。

weakCompareAndSet有可能不是原子性的去更新值,这取决于虚拟机的实现。

@HotSpotIntrinsicCandidate 标注的方法,在HotSpot中都有一套高效的实现,该高效实现基于CPU指令,运行时,HotSpot维护的高效实现会替代JDK的源码实现,从而得到更高的效率。

也就是说HotSpot可能会手动实现这个方法。

@PolymorphicSignature
@HotSpotIntrinsicCandidate
public final native boolean compareAndSet(Object... var1);

@PolymorphicSignature
@HotSpotIntrinsicCandidate
public final native boolean weakCompareAndSet(Object... var1);

其实这个方法和上面的 lazySet 有殊途同归之妙。

小结

咱们对 AtomicInteger 源码进行了初步的分析,底层也确实是依赖 volatile+CAS 实现。

不过发现了两个有趣的实现:weakCompareAndSet 和 lazySet。

看起来反其道而行之,实际上都是出于更高的性能考虑。

文中不少方法都是 native 实现,这让咱们读起来不够尽兴,说到底这个世界上本没有高级语言,只有C语言,和对C语言的封装

但愿本文对你有帮助,若是有其余想法的话,也能够评论区和你们分享哦。

各位极客的点赞收藏转发,是老马写做的最大动力!

深刻学习

相关文章
相关标签/搜索