本文最后更新于:2022年6月11日 下午
CAS
CAS,Compare And Swap,即比较并交换。整个AQS同步组件(例:ReentrantLock获取锁的时候)、Atomic原子类操作等等都是以CAS实现的,ConcurrentHashMap在1.8的版本中也调整为了CAS+Synchronized。
定义
在CAS中有三个参数:内存值V、旧的预期值A、要更新的值B,当且仅当内存值V的值等于旧的预期值A时才会将内存值V的值修改为B,否则什么都不干。
ReentrantLock获取锁(AtomicInteger也用到了):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 final void lock () { if (compareAndSetState(0 , 1 )) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1 ); }final void lock () { acquire(1 ); }private static final Unsafe unsafe = Unsafe.getUnsafe();private static final long stateOffset;protected final boolean compareAndSetState (int expect, int update) { return unsafe.compareAndSwapInt(this , stateOffset, expect, update); }public final native boolean compareAndSwapInt (Object var1, long var2, int var4, int var5) ;
Unsafe是CAS的核心类,提供了硬件级的原子操作,Java无法直接访问底层操作系统,而是通过本地(native)方法来访问。
CAS的缺点 如果自旋CAS长时间地不成功,则会给CPU带来非常大的开销。在JUC中有些地方就限制了CAS自旋的次数,例如BlockingQueue的SynchronousQueue。也可以自己设置可以使用-XX:PreBlockSpin
来更改。
ABA问题,解决方案则是版本号,Java提供了AtomicStampedReference来解决。Pair为AtomicStampedReference的内部类,主要用于记录引用和版本戳信息(标识)。版本戳为int型,保持自增。同时Pair是一个不可变对象,其所有属性全部定义为final,对外提供一个of方法,该方法返回一个新建的Pari对象。pair对象定义为volatile,保证多线程环境下的可见性。
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 ublic class Test { private static AtomicInteger atomicInt = new AtomicInteger (100 ); private static AtomicStampedReference atomicStampedRef = new AtomicStampedReference (100 , 0 ); public static void main (String[] args) throws InterruptedException { Thread intT1 = new Thread (new Runnable () { @Override public void run () { atomicInt.compareAndSet(100 , 101 ); atomicInt.compareAndSet(101 , 100 ); } }); Thread intT2 = new Thread (new Runnable () { @Override public void run () { try { TimeUnit.SECONDS.sleep(1 ); } catch (InterruptedException e) { } boolean c3 = atomicInt.compareAndSet(100 , 101 ); System.out.println(c3); } }); intT1.start(); intT2.start(); intT1.join(); intT2.join(); Thread refT1 = new Thread (new Runnable () { @Override public void run () try { TimeUnit.SECONDS.sleep(1 ); } catch (InterruptedException e) { } atomicStampedRef.compareAndSet(100 , 101 , atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1 ); atomicStampedRef.compareAndSet(101 , 100 , atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1 ); } }); Thread refT2 = new Thread (new Runnable () { @Override public void run () { int stamp = atomicStampedRef.getStamp(); try { TimeUnit.SECONDS.sleep(2 ); } catch (InterruptedException e) { } boolean c3 = atomicStampedRef.compareAndSet(100 , 101 , stamp, stamp + 1 ); System.out.println(c3); } }); refT1.start(); refT2.start(); } }
只能保证一个共享变量的原子操作,Java从1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,可以把多个变量放在一个对象里来进行CAS操作。