Atomic原子类位于java.util.concurrent.atomic包下面,支持单个变量上解除锁的线程安全编程,此包中的类可以将vlatile值、字段和数组的元素概念扩展到那些以提供原子条件更换操作的类中。
对于原子操作,确切说应该是计算机硬件层面提供的原语,它是由一系列计算机指令组合而成的程序,具有不可分割特性,换句话说一旦开始就会一直运行到结束,中间不会被线程调度机制打断。
Atomic的summarypackage.html
可以将包中的类分为五类:
AtomicBoolean、AtomicInteger、AtomicLong 和 AtomicReference 的实例各自提供对相应类型单个变量的原子方式访问和更新功能。例如 AtomicBoolean 提供对int类型单个变量的原子方式访问和更新功能。每个类也为该类型提供适当的实用工具方法。例如,类 AtomicLong 和 AtomicInteger 提供了原子增量方法,可以用于生成序列号。
AtomicStampedRerence 维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。AtomicMarkableReference 维护带有标记位的对象引用,可以原子方式对其进行更新。
AtomicIntegerArray、AtomicLongArray 和 AtomicReferenceArray 类进一步扩展了原子操作,对这些类型的数组提供了支持。例如 AtomicIntegerArray 是可以用原子方式更新其元素的int数组。
AtomicReferenceFieldUpdater、AtomicIntegerFieldUpdater和AtomicLongFieldUpdater是基于反射的实用工具,可以提供对关联字段类型的访问。例如AtomicIntegerFieldUpdater可以对指定类的指定volatile int字段进行原子更新。
DoubleAccumulator、LongAccumulator、DoubleAdder、LongAdder是JDK1.8新增的部分,是对AtomicLong等类的改进。比如LongAccumulator与LongAdder在高并发环境下比AtomicLong更高效。
上面讲到基本数据类型的原子类有3种,AtomicInteger、AtomicBoolean、AtomicLong,以下以AtomicInteger为例讲解下,因为三者内部都是使用了CAS原语,以下为三者的区别:
1、AtomicBoolean内部将1作为true,0作为false,所以内部实现还是跟AtomicInteger一样; 2、AtomicLong是对长整形进行的原子操作,在32位操作系统中,64位的long 和 double变量由于会被JVM当作两个 分离的32位来进行操作,所以不具有原子性。而使用AtomicLong能让long的操作保持原子型。 3、jdk1.8之后出现了LongAdder可代替AtomicLong 复制代码
为什么只有这三种类型? 因为够用了,下面是摘抄子Atomic的summary
Additionally, classes are provided only for those types that are commonly useful in intended applications. For example, there is no atomic class for representing byte. In those infrequent cases where you would like to do so, you can use an AtomicInteger to hold byte values, and cast appropriately. You can also hold floats using Float.floatToRawIntBits(float) and Float.intBitsToFloat(int) conversions, and doubles using Double.doubleToRawLongBits(double) and Double.longBitsToDouble(long) conversions.
AtomicInteger内部包含重要的3个属性(除了序列化的serialVersionUID),分别是Unsafe的引用,在内存中对象的偏移量valueOffset,以及指向正真值的value,这里的value使用了volatile修饰,目的是为了值在内存和高速缓存的可见性。
// 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; 复制代码
AtomicInteger的构造函数也十分简单,除了默认的构造函数之外,还有一个传入初始值的构造器。
public AtomicInteger(int initialValue) { value = initialValue; } /** * Creates a new AtomicInteger with initial value {@code 0}. */ public AtomicInteger() { } 复制代码
下面列举AtomicInteger常用的方法(个人认为:对于原子类值就是它们的状态)
//传递希望当前内存中状态应该是expect值,然后修改为update public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } //这里和compareAndSet一样,据说在jdk8之后又变化 public final boolean weakCompareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } //先返回旧值,然后再+1 public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); } //和getAndIncrement相反,先返回旧值,再-1 public final int getAndDecrement() { return unsafe.getAndAddInt(this, valueOffset, -1); } /** *这里是返回+1后的值,所以先执行Unsafe的getAndAddInt加1,然后返回旧值的同时再+1 */ public final int incrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, 1) + 1; } ...省略其他方法(其中还包括3个1.8之后新增的IntUnaryOperator函数式编程接口) 复制代码
从上面列举出来的方法可以看到,AtomicInteger内部实际调用的式Unsafe.compareAndSwapInt, 注意方法名虽然是compareAndSet,但实际内部在Unsafe调用的是compareAndSwapInt, 由于内部是Native方法,就没再确定究竟是使用了CompareAndSwap还是CompareAndSet哪个原语,看返回值应该是CompareAndSet,但是看方法名确实CompareAndSwap,这两个原语的区别在于返回值不同。
但是很明显,在AtomicInteger内部应该是使用了CAS操作来保证原子性。在其中一个方法getAndIncrement中,进入内部可以看到也是使用了CAS操作
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; } 复制代码
从上述代码中我们可以得出,会先获取内存中存储的值,最终会调用compareAndSetInt()方法来完成最终的原子操作。其中compareAndSetInt()方法的返回值代表着该次CAS操作是否成功。如果不成功。那么会一直循环。直到成功为止(也就是循环CAS操作)。
在一个死循环内不断的尝试修改目标的值,直到修改成功。如果在竞争不激烈的情况下,它修改成功的概率很高,否则的话修改失败的概率就会很高, 在大量修改失败的时候这些原子操作就会多次循环尝试, 因此性能就会受到影响。
这里简要的对CAS操作进行描述:CAS操作内部实现原理是缓存锁,在其操作期间,会修改对应操作对象的内存地址。同时其会保证各个处理器的缓存是一致的,如果处理器发现自己的数据对应的内存地址被修改,就会将当前缓存的数据处理为无效,同时该处理器会重新从系统内存中把数据处理到缓存中。这就是解决缓存一致性的MESI。