引言
介绍完了 Synchronized 就不得不提另外一套锁机制 —— Lock 家族。本系列文章照旧分为上、中、下三篇,分别介绍 CAS、ReetrantLock、AQS 共享锁。
CAS
从 Synchronized 源码中我们可以看到其中大量运用了 CAS 操作,那么什么是 CAS 呢?CAS 全称 Compare And Swap,它其实是无锁策略的实现,主要依赖于 CPU 原子指令的实现。
核心思想 在 Synchronized 的源码中,我们经常会看到这样的语句:
1 markOop res_mark = (markOop) Atomic::cmpxchg_ptr (prototype_header, obj->mark_addr (), mark)
想必大家已经很熟悉了,它的意思就是:比较 obj->mark_addr()
地址指向的内容是否是 mark,如果是则将该地址的内容指向 prototype_header
然后返回原地址的内容 mark
。
而 Java 中,主要的使用方式如下:
1 2 AtomicInteger ai = new AtomicInteger(10 ); boolean isSuccess = ai.compareAndSet(10 , 12 );
如上所示,先创建一个值为 10 的原子化类,然后调用该类的 compareAndSet
方法,如果原有的值就是 10,那么就将该值改为 12。如果替换成功那么返回 true,否则返回 false。
从以上两个示例我们可以了解到,Compare And Swap 中的 Compare 比较的就是替换地址原有的值与你期望的值,如果两者相等,说明当前没有其他线程进行修改,此时就可以进行 Swap 操作。
那么可能你会问了,如果比较之后,其他线程修改了该值呢?其实这点不用担心,因为 CAS 借助的是 CPU 原子指令。
从另外一个角度来说,CAS 其实属于乐观派,之所以不提锁,是因为 CAS 无任何加锁行为。为什么说是乐观派呢?是因为它始终假定原地址没有变更。如果当前线程竞争比较激烈,那么乐观派的 CAS 就不再是考虑的范畴了,此时就需要锁的介入。
Unsafe 介绍 Java 层的 CAS 操作就不得不提位于 sun.misc
包中的 Unsafe 类。CAS 的底层操作都是交由 Unsafe 来实现。该类之所以称为不安全,是因为它中的方法都跟底层有关如:可以直接操作内存,不受 JVM 管理,意味着需要自己释放内存;不少方法需要提供原始地址以及被替换对象的地址,一旦出现问题就会导致 JVM 程序崩溃等。
既然 Unsafe 类的方法可以直接操作内存,那也就意味着它的运行速度要比普通的 Java 程序快,也就更适合高并发场景。
操作分类
Unsafe 是一系列执行底层代码的方法集合,我们可以按照操作的类型对其进行分类。
类、对象、变量的操作 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 public native Object allocateInstance (Class<?> cls) throws InstantiationException ; public native Class<?> defineClass(String name, byte [] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain); public native Class<?> defineAnonymousClass( Class<?> hostClass, byte [] data, Object[] cpPatches); public native long objectFieldOffset (Field f) ;public native long staticFieldOffset (Field f) ;public native int getInt (Object o, long offset) ;public native void putInt (Object o, long offset, int x) ;public native int getIntVolatile (Object o, long offset) ;public native void putIntVolatile (Object o, long offset, int x) ;...
内存的操作 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 public native long allocateMemory (long bytes) ;public native long reallocateMemory (long address, long bytes) ;public native void freeMemory (long address) ;public native void setMemory (Object o, long offset, long bytes, byte value) ;public native void copyMemory (Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes) ; public native int getInt (long address) ;public native void putInt (long address, int x) ;
数组的操作 1 2 3 4 5 6 7 8 9 10 public native int arrayBaseOffset (Class<?> arrayClass) ;public native int arrayIndexScale (Class<?> arrayClass) ;
CAS 操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public final native boolean compareAndSwapObject (Object o, long offset, Object expected, Object x) ;public final native boolean compareAndSwapInt (Object o, long offset, int expected, int x) ; public final native boolean compareAndSwapLong (Object o, long offset, long expected, long x) ;
除此之外,JDK 8 还提供了基于以上三种 CAS 操作的变种,这里就以其中一个方法为例进行说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 public final long getAndAddLong (Object o, long offset, long delta) { long v; do { v = getLongVolatile(o, offset); } while (!compareAndSwapLong(o, offset, v, v + delta)); return v; }
内存屏障
有 CAS 操作,那对于内存屏障的操作肯定也必不可少。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public native void loadFence () ;public native void storeFence () ;public native void fullFence () ;
对于更多的 API 这里就不再介绍了。
底层实现 我们看到大部分方法都是 native
修饰的,也就是说都是本地方法,既然讲到这里了,那不妨在深挖一下看看底层是如何实现的。
当然了,为了不偏移文章主题,这里只介绍 CAS 操作的底层实现。其他 Unsafe 包下的源码大家可以按照同样的思路来分析。
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 UNSAFE_ENTRY (jboolean, Unsafe_CompareAndSwapObject (JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject e_h, jobject x_h)) UnsafeWrapper ("Unsafe_CompareAndSwapObject" ); oop x = JNIHandles::resolve (x_h); oop e = JNIHandles::resolve (e_h); oop p = JNIHandles::resolve (obj); HeapWord* addr = (HeapWord *)index_oop_from_field_offset_long (p, offset); oop res = oopDesc::atomic_compare_exchange_oop (x, addr, e, true ); jboolean success = (res == e); if (success) update_barrier_set ((void *)addr, x); return success; UNSAFE_END UNSAFE_ENTRY (jboolean, Unsafe_CompareAndSwapInt (JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) UnsafeWrapper ("Unsafe_CompareAndSwapInt" ); oop p = JNIHandles::resolve (obj); jint* addr = (jint *) index_oop_from_field_offset_long (p, offset); return (jint)(Atomic::cmpxchg (x, addr, e)) == e; UNSAFE_END UNSAFE_ENTRY (jboolean, Unsafe_CompareAndSwapLong (JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x)) UnsafeWrapper ("Unsafe_CompareAndSwapLong" ); Handle p (THREAD, JNIHandles::resolve(obj)) ; jlong* addr = (jlong*)(index_oop_from_field_offset_long (p (), offset)); if (VM_Version::supports_cx8 ()) return (jlong)(Atomic::cmpxchg (x, addr, e)) == e; else { jboolean success = false ; ObjectLocker ol (p, THREAD) ; if (*addr == e) { *addr = x; success = true ; } return success; } UNSAFE_END
以上存在两种 CAS,oopDesc::atomic_compare_exchange_oop
跟 Atomic::cmpxchg
,我们看下两者有什么不同:
oopDesc::atomic_compar_exchange_oop 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 inline oop oopDesc::atomic_compare_exchange_oop (oop exchange_value, volatile HeapWord *dest, oop compare_value, bool prebarrier) { if (UseCompressedOops) { if (prebarrier) { update_barrier_set_pre ((narrowOop*)dest, exchange_value); } narrowOop val = encode_heap_oop (exchange_value); narrowOop cmp = encode_heap_oop (compare_value); narrowOop old = (narrowOop) Atomic::cmpxchg (val, (narrowOop*)dest, cmp); return decode_heap_oop (old); } else { if (prebarrier) { update_barrier_set_pre ((oop*)dest, exchange_value); } return (oop)Atomic::cmpxchg_ptr (exchange_value, (oop*)dest, compare_value); } }
如果未启用指针压缩则通过 Atomic::cmpxchg_ptr
完成 CAS,如果启用指针压缩,那么执行 Atomic::cmpxchg
。
Atomic::cmpxchg Atomic::cmpxchg
根据不同的系统会有所差异,这里我们以 Linux 为例进行说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #define LOCK_IF_MP(mp) "cmp $0, " #mp "; je 1f; lock; 1: " inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) { int mp = os::is_MP (); __asm__ volatile (LOCK_IF_MP(%4 ) "cmpxchgl %1,(%3)" : "=a" (exchange_value) : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp) : "cc" , "memory" ) ; return exchange_value; }
汇编扩展以 __asm__
开头表示后面部分为汇编,volatile
严禁将此处的汇编语句和其他语句进行重组优化。整个语法包含四部分,之间使用 :
分隔,基本形式如下:
1 2 3 4 5 6 __asm__( "指令,每条指令之后最好使用 \n\t 分隔" :输出寄存器,() 内为 C 程序值 :输入寄存器,() 内为 C 程序值 :会被修改的寄存器 )
LOCK_IF_MP
为宏定义,包含了多条分号分割的汇编指令。从注释中可以看出如果运行在多核处理器上则需要添加 lock 前缀,否则只需要执行 cmpxchgl
。而 lock 前缀属于内存屏障,它的作用是在执行后面指令的过程中锁住总线或者缓存行以保证一致性。
以上汇编最终的格式如下:
1 2 3 4 5 6 7 cmp $0, #mp je 1f lock 1:cmpxchgl %1,(%3) : "=a" (exchange_value) : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp) : "cc", "memory"
我们来翻译以上内容:
cmp 是比较命令,$0 代表立即数 0。如果 mp 等于 0,那么将 ZF(零标志)位置为 1。
je 比较跳转指令,如果 ZF 等于 1 那么就跳转至 1 标签处(f 表示 forward 向前,b 表示 backword 向后)。
lock 表示是否要锁住总线以执行后面的指令。
1: 相当于标签,cmpxchgl %1,(%3) 则是标签对应的指令。% 后边跟的数字代表输出和输入寄存器顺序编号,顺序从输出寄存器序列从左到右,从上到下以 %0 开始一直到 %9 10 个寄存器。示例中,输出的 eax 为 %0,输出的四个参数 exchange_value、compare_value、dest、mp 分别为 %1、%2、%3、%4。
而 cmpxchgl %1,(%3) 也就等价于 cmpxchgl exchange_value,(dest),(dest) 表示 dest 地址所存的值,而 cmpxchgl 还有一个隐含操作数 eax,也就是说先比较输入的 eax 值(compare_value)是否和 dest 所存的值相等,如果相等则把 %1 (exchange_value) 的值写入 dest 所指向的地址。如果不想等则把 dest 地址所存的值写入 eax。
作为输出中的 =a(exchange_value)
指令,= 表示输出,a 表示 eax 寄存器,整个指令则表示把 eax 中的值写入 exchange_value 变量中。所以回归到 (jint)(Atomic::cmpxchg(x, addr, e)) == e
函数,如果 eax 值与 dest 值相等,就会把 eax 存的输入值 compare_value 赋值给输出的 exchange_value,那么函数就会返回 true。如果不想等,则把 dest 指向的值赋值给 eax 进而赋值给 exchange_value 函数最终返回 false。
另外,输入中的 r 表示任意寄存器,cc 则告诉编译器该指令的执行会影响到标志寄存器,要每次重新读取。memory 告诉编译器该指令需要重新从内存中读取最新值而不是从缓存了的寄存器中读取。
使用 介绍完了 Unsafe
类,那么该如何使用它?其实我们只要能获取 Unsafe
的实例就行了,不过从代码中我们看到 Unsafe
的构造方法都是私有的,也就是说外界无法获取。不过它对外提供了 getUnsafe()
方法:
1 2 3 4 5 6 7 8 9 10 private Unsafe () {}private static final Unsafe theUnsafe = new Unsafe();@CallerSensitive public static Unsafe getUnsafe () { Class<?> caller = Reflection.getCallerClass(); if (!VM.isSystemDomainLoader(caller.getClassLoader())) throw new SecurityException("Unsafe" ); return theUnsafe; }
原子操作包
有了 CAS 及 Unsafe 类的基础之后,我们就可以介绍 JUC 并发包提供的 atomic 类了。
基本类型原子操作类 atomic 包针对基本类型提供了 AtomicBoolean、AtomicInteger、AtomicLong 三个,它们的实现大同小异,这里我们以 AtomicLong 为例进行说明。
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 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 public class AtomicLong extends Number implements java .io .Serializable { private static final long serialVersionUID = 1927816293512124184L ; private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8(); private static native boolean VMSupportsCS8 () ; private volatile long value; static { try { valueOffset = unsafe.objectFieldOffset (AtomicLong.class.getDeclaredField("value" )); } catch (Exception ex) { throw new Error(ex); } } public AtomicLong (long initialValue) { value = initialValue; } public AtomicLong () { } public final long get () { return value; } public final void set (long newValue) { value = newValue; } public final void lazySet (long newValue) { unsafe.putOrderedLong(this , valueOffset, newValue); } public final long getAndSet (long newValue) { return unsafe.getAndSetLong(this , valueOffset, newValue); } public final boolean compareAndSet (long expect, long update) { return unsafe.compareAndSwapLong(this , valueOffset, expect, update); } public final boolean weakCompareAndSet (long expect, long update) { return unsafe.compareAndSwapLong(this , valueOffset, expect, update); } public final long getAndIncrement () { return unsafe.getAndAddLong(this , valueOffset, 1L ); } public final long getAndDecrement () { return unsafe.getAndAddLong(this , valueOffset, -1L ); } public final long getAndAdd (long delta) { return unsafe.getAndAddLong(this , valueOffset, delta); } public final long incrementAndGet () { return unsafe.getAndAddLong(this , valueOffset, 1L ) + 1L ; } public final long decrementAndGet () { return unsafe.getAndAddLong(this , valueOffset, -1L ) - 1L ; } public final long addAndGet (long delta) { return unsafe.getAndAddLong(this , valueOffset, delta) + delta; } public final long updateAndGet (LongUnaryOperator updateFunction) { long prev, next; do { prev = get(); next = updateFunction.applyAsLong(prev); } while (!compareAndSet(prev, next)); return next; } public final long getAndAccumulate (long x, LongBinaryOperator accumulatorFunction) { long prev, next; do { prev = get(); next = accumulatorFunction.applyAsLong(prev, x); } while (!compareAndSet(prev, next)); return prev; } public final long accumulateAndGet (long x, LongBinaryOperator accumulatorFunction) { long prev, next; do { prev = get(); next = accumulatorFunction.applyAsLong(prev, x); } while (!compareAndSet(prev, next)); return next; } public String toString () { return Long.toString(get()); } public int intValue () { return (int )get(); } public long longValue () { return get(); } public float floatValue () { return (float )get(); } public double doubleValue () { return (double )get(); } }
是不是有了 CAS 及 Unsafe 的基础再来看 AtomicLong 的实现就简单很多了?
引用类型原子操作类 原理类似,直接上代码:
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 public class AtomicReference <V > implements java .io .Serializable { private static final long serialVersionUID = -1848883965231344442L ; private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicReference.class.getDeclaredField("value" )); } catch (Exception ex) { throw new Error(ex); } } private volatile V value; public final V getAndAccumulate (V x, BinaryOperator<V> accumulatorFunction) { V prev, next; do { prev = get(); next = accumulatorFunction.apply(prev, x); } while (!compareAndSet(prev, next)); return prev; } ... }
与基础类型的操作大同小异无非就是将值类型换成了引用类型。
数组类型原子操作类 针对于数组类型,atomic 包提供了AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray 三种类型。
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 public class AtomicIntegerArray implements java .io .Serializable { private static final long serialVersionUID = 2862133569453604235L ; 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 scale = unsafe.arrayIndexScale(int [].class); if ((scale & (scale - 1 )) != 0 ) throw new Error("data type scale not a power of two" ); shift = 31 - Integer.numberOfLeadingZeros(scale); } private static long byteOffset (int i) { return ((long ) i << shift) + base; } private long checkedByteOffset (int i) { if (i < 0 || i >= array.length) throw new IndexOutOfBoundsException("index " + i); return byteOffset(i); } public final int getAndSet (int i, int newValue) { return unsafe.getAndSetInt(array, checkedByteOffset(i), newValue); } ... }
属性更新原子操作类 我们依然可以使用原子操作类来更新对象中的某个属性以满足多线程安全。atomic 包提供了 AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater 三种类型的属性更新。
我们以 Integer 类型的属性更新器为例进行说明:
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 public abstract class AtomicIntegerFieldUpdater <T > { @CallerSensitive public static <U> AtomicIntegerFieldUpdater<U> newUpdater (Class<U> tclass, String fieldName) { return new AtomicIntegerFieldUpdaterImpl<U> (tclass, fieldName, Reflection.getCallerClass()); } private static final class AtomicIntegerFieldUpdaterImpl <T > extends AtomicIntegerFieldUpdater <T > { private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); private final long offset; private final Class<?> cclass; private final Class<T> tclass; AtomicIntegerFieldUpdaterImpl(final Class<T> tclass, final String fieldName, final Class<?> caller) { final Field field; final int modifiers; try { field = AccessController.doPrivileged( new PrivilegedExceptionAction<Field>() { public Field run () throws NoSuchFieldException { return tclass.getDeclaredField(fieldName); } }); modifiers = field.getModifiers(); sun.reflect.misc.ReflectUtil.ensureMemberAccess( caller, tclass, null , modifiers); ClassLoader cl = tclass.getClassLoader(); ClassLoader ccl = caller.getClassLoader(); if ((ccl != null ) && (ccl != cl) && ((cl == null ) || !isAncestor(cl, ccl))) { sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass); } } catch (PrivilegedActionException pae) { throw new RuntimeException(pae.getException()); } catch (Exception ex) { throw new RuntimeException(ex); } if (field.getType() != int .class) throw new IllegalArgumentException("Must be integer type" ); if (!Modifier.isVolatile(modifiers)) throw new IllegalArgumentException("Must be volatile type" ); this .cclass = (Modifier.isProtected(modifiers) && tclass.isAssignableFrom(caller) && !isSamePackage(tclass, caller)) ? caller : tclass; this .tclass = tclass; this .offset = U.objectFieldOffset(field); } private final void accessCheck (T obj) { if (!cclass.isInstance(obj)) throwAccessCheckException(obj); } public final boolean compareAndSet (T obj, int expect, int update) { accessCheck(obj); return U.compareAndSwapInt(obj, offset, expect, update); } ... } ... }
与其他原子类的操作相似,唯一不同的是对于字段的校验比较严格:
字段必须是对应的类型(如:针对于 Integer 的属性更新,字段就必须是 int 类型)。
字段必须是 volatile,毕竟需要保证内存可见性。
字段必须对当前的 updater 可见(如果不是发生在当前类内部的原子操作则不能使用 private、protected 修饰符;如果是子类修改父类字段那么操作符必须是 protected 以上;如果同属一个 package 则必须满足 default 以上)。
除此之外还有一些隐式的规则:
字段不能被 final 修饰,毕竟常量不可更改。
字段不能被 static 修饰,因为修改的是对象字段而不是类字段。
CAS 如此完美吗? 无锁的 CAS 就完美了吗?其实并非如此!从它的实现上来看,虽然说与期望值一致那么说明并没有线程参与竞争或者说该值没有修改过并不严谨。因为还有可能该值已经被其他线程修改了,只不过在进行比较的时候又再一次修改回了原值,也就是所谓的 ABA 问题。
那么 CAS 如果解决 ABA 问题呢?有两种方案:
基于时间戳控制的 AtomicStampedReference。
基于 boolean 值控制的 AtomicMarkableReference。
AtomicStampedReference 我们看下 AtomicStampedReference 如何完美解决 ABA 问题。
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 public class AtomicStampedReference <V > { private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); private static final long pairOffset = objectFieldOffset(UNSAFE, "pair" , AtomicStampedReference.class); private volatile Pair<V> pair; public AtomicStampedReference (V initialRef, int initialStamp) { pair = Pair.of(initialRef, initialStamp); } public boolean compareAndSet (V expectedReference, V newReference, int expectedStamp, int newStamp) { Pair<V> current = pair; return expectedReference == current.reference && expectedStamp == current.stamp && ((newReference == current.reference && newStamp == current.stamp) || casPair(current, Pair.of(newReference, newStamp))); } private boolean casPair (Pair<V> cmp, Pair<V> val) { return UNSAFE.compareAndSwapObject(this , pairOffset, cmp, val); } 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); } } }
因为有了时间戳的参与,所以也就完美解决了 ABA 问题。
AtomicMarkableReference 与 AtomicStampedReference 维护时间戳的原理不同,AtomicMarkableReference 内部维护 boolean 变量来避免 ABA 问题。也正是如此,通过维护 boolean 状态转换 的 AtomicMarkableReference 并不能完全避免 ABA 问题。
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 public class AtomicMarkableReference <V > { private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); private static final long pairOffset = objectFieldOffset(UNSAFE, "pair" , AtomicMarkableReference.class); private volatile Pair<V> pair; public AtomicMarkableReference (V initialRef, boolean initialMark) { pair = Pair.of(initialRef, initialMark); } public boolean compareAndSet (V expectedReference, V newReference, boolean expectedMark, boolean newMark) { Pair<V> current = pair; return expectedReference == current.reference && expectedMark == current.mark && ((newReference == current.reference && newMark == current.mark) || casPair(current, Pair.of(newReference, newMark))); } private boolean casPair (Pair<V> cmp, Pair<V> val) { return UNSAFE.compareAndSwapObject(this , pairOffset, cmp, val); } 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; }
基于布尔的实现不像时间戳可以一直递增,布尔仍然会存在 ABA 问题。
总结 至此 CAS 就介绍完了,与 Synchronized 相比,CAS 这种自旋的方式只是在逻辑上让当前线程进行阻塞,是一种用户态的行为。并非让线程真正挂起从而进行用户态到内核态的转换,所以它的性能比较好。但是如果竞争比较激烈,自旋一直失败并且到达指定阈值,系统也会让当前线程挂起从而让出 CPU 资源。那么此时 CAS 就不再是优先选择的方案了。