十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Java多线程:CAS从原理到应用

Java多线程:CAS从原理到应用 CAS是Java多线程开发中一个基础概念包括很多其他的中间件都会用到它。那么我们就从原理到应用一起去揭开它的面纱。一、CAS原理CASCompare and Swap比较并交换。是通过原子指令的方式实现在多线程下的同步功能。在实现过程中通过把前后两次获取到的变量在内存中的值进行比对如果相等则对变量值进行更改否则重新读取内存的值然后重复上述过程。在Java中CAS是属于乐观锁的一种也就是那种不进行加锁而是假定在没有冲突的条件下去完成一个操作如果中途发生冲突例如变量的内存值被改动过导致V和A值不相等等情况导致失败后马上进行重试直到成功。既然有乐观锁那么与之对应的就是悲观锁。synchronized 就是悲观锁的一种这种会对作用范围内的资源进行锁定其他线程需要等待所释放才能获取这把锁。二、CAS的不足虽然说CAS的使用广泛但是其会出现三个主要的问题1、ABA问题在做CAS更新操作中读取到变量的值A在准备为变量赋值的时候期望值依旧为A但是变量值可能已被多次修改后充值改回A这个CAS的更新漏洞就被叫做ABA。2、循环时间长开销大自旋CAS的方式如果长时间不成功会给CPU带来很大的开销。3、只能保证一个共享变量的原子操作只对一个共享变量操作可以保证原子性但是多个则不行。三、CAS在Java中的应用Java中大量使用CAS比较集中在java.util.concurrent包内。为了解决ABA问题Java提供多个解决方案1、AtomicStampedReferencepackage multithread.cas; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicStampedReference; public class AtomicReferenceTest { public static void main(String[] args) { boolean result CasUtil.increment(); System.out.println(result:result); result CasUtil.increment(); System.out.println(result:result); } public static class CasUtil { public static AtomicStampedReferenceInteger atomicStampedReference new AtomicStampedReference(1,1); //自增操作 public static boolean increment() { boolean result atomicStampedReference .compareAndSet(atomicStampedReference.getReference(), //expectedReference引用的期望值 atomicStampedReference.getReference()1, atomicStampedReference.getStamp(),//expectedStamp版本戳的期望值 atomicStampedReference.getStamp()1); while(!result) { System.out.println(result is false); return increment(); } return true; } } }设置atomicStampedReference.getReference()和atomicStampedReference.getStamp()这两条语句保证了期望值和原值必然一致所以main方法打印结果一直是“true”。接下来我们分析代码进入到AtomicStampedReference类的compareAndSet方法其获取内存值与传入的期待值比较并返回比较结果。在前三个比较项如有一项不成立(一般是第三项)进行第四个条件操作即调用casPair方法casPair方法调用UNSAFE.compareAndSwapObject。private static final long pairOffset objectFieldOffset(UNSAFE, pair, AtomicStampedReference.class); /** * Atomically sets the value of both the reference and stamp * to the given update values if the * current reference is {code } to the expected reference * and the current stamp is equal to the expected stamp. * * param expectedReference the expected value of the reference * param newReference the new value for the reference * param expectedStamp the expected value of the stamp * param newStamp the new value for the stamp * return {code true} if successful */ public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) { PairV current pair; return expectedReference current.reference //判断旧引用和当前的是否一样判断 expectedStamp current.stamp //旧版本和当前的是否一样 ((newReference current.reference newStamp current.stamp) || //判断引用和版本是否都相等也就是替换和被替换的是同一个对象就不需要再调用casPair了 casPair(current, Pair.of(newReference, newStamp))); } private boolean casPair(PairV cmp, PairV val) { return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);//调用UNSAFE的compareAndSwapObject方法进行对象比较及修改 } private static class PairT { final T reference; final int stamp; private Pair(T reference, int stamp) { this.reference reference; this.stamp stamp; } static T PairT of(T reference, int stamp) { return new PairT(reference, stamp); //每次返回一个新Pair对象 } } static long objectFieldOffset(sun.misc.Unsafe UNSAFE, String field, Class? klazz) { try { return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field)); } catch (NoSuchFieldException e) { // Convert Exception to corresponding Error NoSuchFieldError error new NoSuchFieldError(field); error.initCause(e); throw error; } }UNSAFE类的代码public native long objectFieldOffset(Field var1); public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
返回列表