所谓的原子性表示一个或者多个操作,要么全部执行完,要么一个也不执行。不能出现成功一部分失败一部分的情况。

在多线程中,如果多个线程同时更新一个共享变量,可能会得到一个意料之外的值。比如 i=1 。A 线程更新 i+1 、B 线程也更新 i+1。

通过两个线程并行操作之后可能 i 的值不等于 3。而可能等于 2。因为 A 和 B 在更新变量 i 的时候拿到的 i 可能都是 1这就是一个典型的原子性问题。
cas.jpg

从 JDK1.5 开始,在 J.U.C 包中提供了 Atomic 包,提供了对于常用数据结构的原子操作。它提供了简单、高效、以及线程安全的更新一个变量的方式。

1.juc中的原子操作类

由于变量类型的关系,在 J.U.C 中提供了 12 个原子操作的类。这 12 个类可以分为四大类:

  1. 原子更新基本类型:AtomicBoolean、AtomicInteger、AtomicLong

  2. 原子更新数组:AtomicIntegerArray 、 AtomicLongArray 、AtomicReferenceArray

  3. 原子更新引用:AtomicReference 、 AtomicReferenceFieldUpdater 、AtomicMarkableReference(更新带有标记位的引用类型)

  4. 原子更新字段:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicStampedReference

2.AtomicInterger源码分析

2.1属性&构造器

  1. private static final long serialVersionUID = 6214790243416807050L;
  2. //获取到Unsafe类
  3. private static final Unsafe unsafe = Unsafe.getUnsafe();
  4. //value的内存偏移量
  5. private static final long valueOffset;
  6. //初始化的时候获取value的偏移量
  7. static {
  8. try {
  9. valueOffset = unsafe.objectFieldOffset
  10. (AtomicInteger.class.getDeclaredField("value"));
  11. } catch (Exception ex) { throw new Error(ex); }
  12. }
  13. //具体存放的value值,通过volatile保证内存可见性
  14. private volatile int value;
  15. //创建一个带有初始值的原子类对象
  16. public AtomicInteger(int initialValue) {
  17. value = initialValue;
  18. }
  19. //创建一个默认0值的原子类对象
  20. public AtomicInteger() {
  21. }

2.2 get()&set()&lazySet()

  1. //获取当前最新值
  2. //get 方法只需要直接返回 value 的值就行,这里的 value 是通过 Volatile 修饰的,用来保证可见性。
  3. public final int get() {
  4. return value;
  5. }
  6. //将当前值设置为指定的值
  7. public final void set(int newValue) {
  8. value = newValue;
  9. }
  10. /**
  11. * lazyset调用了unsafe的putOrderedInt方法
  12. * 对比一下set和lazySet:
  13. * 对比汇编指令差异,可以看到set操作加了lock锁,lazySet没有。
  14. * lock锁的含义是什么呢?
  15. * lazySet提供一个store store屏障(在当代系统中是很低成本的操作,或者说没有操作),
  16. * 但是没有store load屏障(这通常是volatile写入动作最昂贵的部分)。
  17. * @param newValue
  18. */
  19. public final void lazySet(int newValue) {
  20. unsafe.putOrderedInt(this, valueOffset, newValue);
  21. }

2.3 getAndIncrement()

getAndIncrement 实际上是调用 unsafe 这个类里面提供的方法,这个类相当于是一个后门,使得 Java 可以像 C 语言的指针一样直接操作内存空间。当然也会带来一些弊端,就是指针的问题。

实际上这个类在很多方面都有使用,除了 J.U.C 这个包以外,还有 Netty、kafka 等等,这个类提供了很多功能,包括多线程同步(monitorEnter)、CAS 操 作 (compareAndSwap) 、 线 程 的 挂 起 和 恢 复(park/unpark)、内存屏(loadFence/storeFence)内存管理(内存分配、释放内存、获取内存地址等.)

  1. public final int getAndIncrement() {
  2. return unsafe.getAndAddInt(this, valueOffset, 1);
  3. }

2.4 getAndAddInt()

通过 do/while 循环,基于 CAS 乐观锁来做原子递增。实际上前面的 valueOffset 的作用就是从主内存中获得当前value 的值和预期值做一个比较,如果相等,对 value 做递增并结束循环。

  1. public final int getAndIncrement() {
  2. return unsafe.getAndAddInt(this, valueOffset, 1);
  3. }

2.5 compareAndSet()

它 提 供 了 compareAndSet , 允 许 客 户 端 基 于AtomicInteger 来实现乐观锁的操作。

  1. public final boolean compareAndSet(int expect, int update) {
  2. return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
  3. }

3.AtomicStampReference

�解决cas的ABA问题锁提供的一个类,解决方法是对数据加版本号。

3.1 属性&构造器

  1. //用volatile来修饰Pair对象保证其内存可见性
  2. private volatile Pair<V> pair;
  3. //通过传入的初始值和版本号构建pair对象
  4. public AtomicStampedReference(V initialRef, int initialStamp) {
  5. pair = Pair.of(initialRef, initialStamp);
  6. }

3.2 内部类

  1. private static class Pair<T> {
  2. final T reference; //引用数据
  3. final int stamp; //版本号
  4. private Pair(T reference, int stamp) {
  5. this.reference = reference;
  6. this.stamp = stamp;
  7. }
  8. //通过泛型方法构造对象
  9. static <T> Pair<T> of(T reference, int stamp) {
  10. return new Pair<T>(reference, stamp);
  11. }
  12. }

3.3 compareAndSet()

比较并交换

  1. //参数:期望值,新值,期望版本,新版本
  2. public boolean compareAndSet(V expectedReference,
  3. V newReference,
  4. int expectedStamp,
  5. int newStamp) {
  6. Pair<V> current = pair;
  7. return
  8. //期望值和当前值相等
  9. expectedReference == current.reference &&
  10. //期望版本和新版本相等
  11. expectedStamp == current.stamp &&
  12. (
  13. (
  14. //表示版本号对应上的同时,值也对应上,就没有必要创建新的pair对象了
  15. newReference == current.reference &&
  16. newStamp == current.stamp
  17. ) ||
  18. //创建新的pair对象
  19. casPair(current, Pair.of(newReference, newStamp))
  20. );
  21. }

3.4 casPair()

实际上调用的还是unsafe类里面的compareAndSwapObject()。

  1. private boolean casPair(Pair<V> cmp, Pair<V> val) {
  2. return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
  3. }