能够原子性更新的 Integer 值,固然这个类并不能彻底替代 Integer 对象。html
使用起来仍是很方便的。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
可恶!这个类使用起来居然这么方便。安全
那么李大狗是如何实现的呢?并发
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;
这是一个 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等并无区别,此处不作过多解释。
为何 AtomicInteger 能保持原子性呢?
咱们一块儿来看一下是如何基于 Unsafe 实现原子性的?
/** * 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 变量来修饰这些共享状态,
由于访问共享状态以前先要得到锁, Lock.lock()方法可以得到锁,而得到锁的操做和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 实现。
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); }
这个方法我以为也颇有趣,弱比较?拿泥搜来。
/** * 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语言的封装。
但愿本文对你有帮助,若是有其余想法的话,也能够评论区和你们分享哦。
各位极客的点赞收藏转发,是老马写做的最大动力!