引言

Java从JDK1.5开始提供了java.util.concurrent.atomic包,方便程序员在多线程环境下,无锁的进行原子操作。原子变量的底层使用了 处理器提供的原子指令,但是不同的CPU架构可能提供的原子指令不一样,也有可能需要某种形式的内部锁,所以该方法不能绝对保证线程 不被阻塞。

CAS(Compare And Swap)比较并交换

CAS算法的过程是这样:它包含3个参数CAS(V,E,N)。V表示要更新的变量(内存值),E表示预期值(旧的),N表示新值。当且仅当V值等于 E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。最后,CAS返回当前V的真实值。 CAS操作是抱着乐观的态度进行的(乐观锁),它总是认为自己可以成功完成操作。当多个线程同时使用CAS操作一个变量时,只有一个会胜出, 并成功更新,其余均会失败。失败的线程不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。基于这样的 原理,CAS操作即时没有锁,也可以发现其他线程对当前线程的干扰,并进行恰当的处理。 CAS 整个操作过程是原子操作,是一条CPU指令完成的

Atomic包介绍

在Atomic包里一共有12个类,如下分几种类型原子类操作:

  • 基本类型:AtomicBoolean,AtomicInteger,AtomicLong
  • 引用类型:AtomicReference
  • 数组类型:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
  • 属性原子修改器类型:AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicReferenceFieldUpdater
  • 解决ABA类型:AtomicMarkableReference,AtomicStampedReference

Atomic包里的类基本都是使用Unsafe实现的包装类。Unsafe.compareAndSwapXXX来实现原子操作。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    /**
     * Atomically update Java variable to <tt>x</tt> if it is currently
     * holding <tt>expected</tt>.
     * @return <tt>true</tt> if successful
     */
    public final native boolean compareAndSwapObject(Object o, long offset,
                                                     Object expected,
                                                     Object x);

    /**
     * Atomically update Java variable to <tt>x</tt> if it is currently
     * holding <tt>expected</tt>.
     * @return <tt>true</tt> if successful
     */
    public final native boolean compareAndSwapInt(Object o, long offset,
                                                  int expected,
                                                  int x);

    /**
     * Atomically update Java variable to <tt>x</tt> if it is currently
     * holding <tt>expected</tt>.
     * @return <tt>true</tt> if successful
     */
    public final native boolean compareAndSwapLong(Object o, long offset,
                                                   long expected,
                                                   long x);

看源码compareAndSwapXXX方法有三个参数,一个参数是一个更新值当前对象,第二参数是修改当前对象属性值偏移量,第三参数当前 属性值(支持类型int,long和Object),第四参数更新值。

第二参数修改属性偏移量获取分两种类型:

  • 基本类型和引用类型获取Unsafe.objectFieldOffset(Field field)方法获取。

    AtomicInteger源代码如下

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
                 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); }
                 }
                          
                 public final boolean compareAndSet(int expect, int update) {
                       return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
                 }
    
  • 数组类型获取相对复杂点。分三步进行

    • 第一步:Unsafe.arrayBaseOffset(Class arrayClass)方法获取数组第一个元素的偏移量。

    • 第二步:Unsafe.arrayIndexScale(Class arrayClass)方法获取数组类型增量因子。

    • 第三步:获取修改数组属性下标偏移值=第一步的数组第一个元素的偏移量+第二步的增量因子(乘以)下标值

      AtomicIntegerArray源码实现跟上面说逻辑类似,如下:

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      
            private static final Unsafe unsafe = Unsafe.getUnsafe();
            //第一步获取数组第一个元素的偏移量
            private static final int base = unsafe.arrayBaseOffset(int[].class);
            private static final int shift;
            private final int[] array;
               
            static {
               //第二步获取数组类型增量因子(int:4,long:8,Object:4)
                int scale = unsafe.arrayIndexScale(int[].class);
                //数据类型不是2的幂
                if ((scale & (scale - 1)) != 0)
                    throw new Error("data type scale not a power of two");
                 /**
                   *numberOfLeadingZeros(int i) 给定一个int类型数据,返回这个数据的二进制串中从最左边算起连续的“0”的
                   *总数量。因为int类型的数据长度为32所以高位不足的地方会以“0”填充。
                   *这里获取一个下标右移增量因子值
                   * shift  int:2,long:3,Object:2
                   */
                shift = 31 - Integer.numberOfLeadingZeros(scale);
            }
               
            private long checkedByteOffset(int i) {
                if (i < 0 || i >= array.length)
                    throw new IndexOutOfBoundsException("index " + i);
               
                return byteOffset(i);
            }
               
            private static long byteOffset(int i) {
                //第三步获取修改数组属性下标偏移值,这里用到右移增量因子值,我猜想可能性能比乘以(scale*i+base)好点。
                return ((long) i << shift) + base;
            }
                   
            public final boolean compareAndSet(int i, int expect, int update) {
                return compareAndSetRaw(checkedByteOffset(i), expect, update);
            }   
            private boolean compareAndSetRaw(long offset, int expect, int update) {
               return unsafe.compareAndSwapInt(array, offset, expect, update);
            }      
      

基本类型

原子更新基本类型三个类:

  • AtomicBoolean:原子更新布尔类型
  • AtomicInteger:原子更新整数类型
  • AtomicLong:原子更新长整数类型

分析AtomicInteger方法源码如下:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
 /**
     * 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;
    }

    /**
     * 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);
    }

    /**
     * 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) {
        for (;;) {
            int current = get();
            if (compareAndSet(current, newValue))
                return current;
        }
    }

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

    /**
     * Atomically increments by one the current value.
     * 以原子操作自增,并返回自增前结果
     * @return the previous value
     */
    public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    /**
     * Atomically decrements by one the current value.
     * 以原子操作自减,并返回自减前结果
     * @return the previous value
     */
    public final int getAndDecrement() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    /**
     * Atomically adds the given value to the current value.
     * 以原子方式将输入的数值与实例中的值(value)相加,并返回相加前结果
     * @param delta the value to add
     * @return the previous value
     */
    public final int getAndAdd(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return current;
        }
    }

    /**
     * Atomically increments by one the current value.
     * 以原子操作自增,并返回自增结果
     * @return the updated value
     */
    public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

    /**
     * Atomically decrements by one the current value.
     * 以原子操作自减,并返回自减结果
     * @return the updated value
     */
    public final int decrementAndGet() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

    /**
     * Atomically adds the given value to the current value.
     * 以原子方式将输入的数值与实例中的值(value)相加,并返回相加结果
     * @param delta the value to add
     * @return the updated value
     */
    public final int addAndGet(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return next;
        }
    }

注意:lazySet方法,说明解释

lazySet是使用Unsafe.putOrderedObject方法,这个方法在对低延迟代码是很有用的,它能够实现非堵塞的写入,这些写入不会被Java的JIT 重新排序指令,这样它使用快速的存储-存储(store-store) barrier, 而不是较慢的存储-加载(store-load) barrier, 后者总是用在volatile的 写操作上,这种性能提升是有代价的,虽然便宜,也就是写后结果并不会被其他线程看到,甚至是自己的线程,通常是几纳秒后被其他线程看到, 这个时间比较短,所以代价可以忍受。

类似Unsafe.putOrderedObject还有unsafe.putOrderedLong等方法,unsafe.putOrderedLong比使用 volatile long要快3倍左右。.

从AmoticInteger和AmoticBoolean源码分析, 其次AtomicBoolean把Boolean转换成整型,做原子操作。 而都调用Unsafe.compareAndSwapInt方法。 AtomicLong源码分析操作Unsafe.compareAndSwapLong方法。

引用类型

AtomicReference:原子操作更新引用类型

AtomicReference源码分析调用Unsafe.compareAndSwapObject方法。

数组类型

  • AtomicIntegerArray:原子操作更新整数数组
  • AtomicLongArray: 原子操作更新长整数数组
  • AtomicReferenceArray: 原子操作更新引用对象数组

AtomicIntegerArray源码查看看,跟AtomicInteger方法基本相同,只是数组多个下标操作。按下标查找相应偏移量原子操作更新相应值。 AtomicLongArray和AtomicReferenceArray原子数组操作,其次按类型原子更新。 AtomicLongArray按long类型原子操作,AtomicReferenceArray 按Object类型原子操作。

属性原子修改器类型

  • AtomicIntegerFieldUpdater:原子操作修改int类型属性
  • AtomicLongFieldUpdater:原子操作修改long类型属性
  • AtomicReferenceFieldUpdater:原子操作修改引用类型属性

属性原子修改器类型通过反射原子更新对象的字段,既然他们的作用是更新字段我们知道有些类型的字段是不可被更新的,所以被更新的 字段是有一定的要求:

  • 必须是volatile类型(volatile是线程可见变量,保存在Jvm的主内存中,而不是线程的工作内存里面)
  • 只能是实例变量,不能是类变量,也就是说不能加static关键字;
  • 只能是可修改变量,不能使final变量,因为final的语义就是不可修改。实际上final的语义和volatile是有冲突的,这两个关键 字不能同时存在,
  • 对于AtomicIntegerFieldUpdater和AtomicLongFieldUpdater只能修改int/long类型的字段,不能修改其包装类型(Integer/Long)。 如果要修改包装类型就需要使用AtomicReferenceFieldUpdater。

AtomicIntegerFieldUpdater源码分析,AtomicIntegerFieldUpdater定义了一些抽象方法,跟普通AtomicInteger一样

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
    private static class AtomicIntegerFieldUpdaterImpl<T> extends AtomicIntegerFieldUpdater<T> {
        private static final Unsafe unsafe = Unsafe.getUnsafe();
        private final long offset;
        private final Class<T> tclass;
        private final Class cclass;

        AtomicIntegerFieldUpdaterImpl(Class<T> tclass, String fieldName, Class<?> caller) {
            Field field = null;
            int modifiers = 0;
            try {
                field = tclass.getDeclaredField(fieldName);
                modifiers = field.getModifiers();
                sun.reflect.misc.ReflectUtil.ensureMemberAccess(
                    caller, tclass, null, modifiers);
                sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }

            Class fieldt = field.getType();
            //判断类型(int long 引用类型)
            if (fieldt != int.class)
                throw new IllegalArgumentException("Must be integer type");
           //判断volatitle修饰
            if (!Modifier.isVolatile(modifiers))
                throw new IllegalArgumentException("Must be volatile type");

            this.cclass = (Modifier.isProtected(modifiers) &&
                           caller != tclass) ? caller : null;
            this.tclass = tclass;
            //获取对象属性偏移量
            offset = unsafe.objectFieldOffset(field);
        }

AtomicLongFieldUpdater这块有不同的就是要判断一下硬件底层是否支持longCAS指令。

1
2
3
4
5
6
7
8
    public static <U> AtomicLongFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName) {
        Class<?> caller = Reflection.getCallerClass();
        //判断是否支持long CAS指令
        if (AtomicLong.VM_SUPPORTS_LONG_CAS)
            return new CASUpdater<U>(tclass, fieldName, caller);  //CAS
        else
            return new LockedUpdater<U>(tclass, fieldName, caller); //同步锁
    }

AtomicReferenceFieldUpdater这块AtomicReference很类似,没主体讲解。

解决ABA类型

ABA问题:假设两个线程T1和T2访问同一个变量V,当T1访问变量V时,读取到V的值为A;此时线程T1被抢占了,T2开始执行,T2先将变量 V的值从A变成了B,然后又将变量V从B变回了A;此时T1又抢占了主动权,继续执行,它发现变量V的值还是A,以为没有发生变化,但是实际 上却变化了。这个过程中,变量V从A变为B,再由B变为A就被形象地称为ABA问题了。

解决ABA问题实现两个类如下:

  • AtomicMarkableReference:[reference, boolean] pairs.对象原子操作,用布尔类型标记解决ABA问题
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
     private static class Pair<T> {
         final T reference;
         final boolean mark;
         private Pair(T reference, boolean mark) {
             this.reference = reference;
             this.mark = mark;
         }
         static <T> Pair<T> of(T reference, boolean mark) {
             return new Pair<T>(reference, mark);
         }
     }
 
     private volatile Pair<V> pair;
  • AtomicStampedReference:[reference, integer] pairs.对象原子操作,用整数标记解决ABA问题。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
 private static class Pair<T> {
        final T reference;
        final int stamp;
        private Pair(T reference, int stamp) {
            this.reference = reference;
            this.stamp = stamp;
        }
        static <T> Pair<T> of(T reference, int stamp) {
            return new Pair<T>(reference, stamp);
        }
    }

    private volatile Pair<V> pair;