java并发线程透彻理解CAS以及ABA问题的处理
2023-04-21 金融
以上标识符测试结果如下:
1-3-2、常用reentrantLock常用ReentrantLock的时候一定要注意到,要将unlock()放入finally标识符块中的,可避免业务标识符所致,不能拘禁针
public class ThreadLockReentrantLock { private volatile static int count = 0; static ReentrantLock reentrantLock=new ReentrantLock(); public static void main(String[] args) { for (int i = 0; i < 10; i++) { Thread thread = new Thread(new Runnable() { @Override public void run() { //常用reentrantLock.lock();透过加针转换 reentrantLock.lock(); try { for (int j = 0; j < 10000; j++) { count++; } } finally { //reentrantLock.unlock();一定要放入finally中的,可避免业务标识符所致,避免针不拘禁 reentrantLock.unlock(); } } }); thread.start(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("count:" + count); }}1-3-3、常用CAS1-3-3-1、通过折射给予UnsafeUnsafe是jdk获取的工具类,我们要常用无需通过折射有助于取到,同时给予其获取倍数的转换
public class UnsafeFactory { /** * 获取 Unsafe 普通人 * @return */ public static Unsafe getUnsafe() { try { Field field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); return (Unsafe) field.get(null); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 获取字符串的缓可知倍数 * @param unsafe * @param clazz * @param fieldName * @return */ public static long getFieldOffset(Unsafe unsafe, Class clazz, String fieldName) { try { return unsafe.objectFieldOffset(clazz.getDeclaredField(fieldName)); } catch (NoSuchFieldException e) { throw new Error(e); } }}1-3-3-2、通过CAS借助对表达式的重写首再行假设一个普通人Entity,其中的假设一个int各种类型表达式X,然后通过CAS对X透过重写。
public class CASTest { public static void main(String[] args) { Entity entity = new Entity(); //通过折射有助于给予Unsafe Unsafe unsafe = UnsafeFactory.getUnsafe(); //给予x缓可知中的的倍数 long offset = UnsafeFactory.getFieldOffset(unsafe, Entity.class, "x"); System.out.println(offset); boolean successful; // 4个参数分别是:普通人模板、字符串的缓可知倍数、字符串原最大值、字符串预览最大值 //通过CAS将x由0改名3 successful = unsafe.compareAndSwapInt(entity, offset, 0, 3); System.out.println(successful + " " + entity.x); //通过CAS将x由3改名5 successful = unsafe.compareAndSwapInt(entity, offset, 3, 5); System.out.println(successful + " " + entity.x); //通过CAS将x由3改名8---本条是不正式成立的,纸片之前将x改名5 successful = unsafe.compareAndSwapInt(entity, offset, 3, 8); System.out.println(successful + " " + entity.x); }}class Entity{ int x;}运行结果如下:
首再行给予X的倍数为12,其次就是对x透过重写的结果打印。第三次重写惨败,因为第二次之前将x改名5,如果在用第一次的3作为原最大值去重写,就则会重写惨败。
1-3-3-3、CAS在结构上根据以上可执行结果,就证明了CAS的在结构上:再行更为、后预览,这两步,底层则会借助我们借助水分子转换,有序性和可见性避免公测文切换。
1-4、CAS源码分析1-4-1、java层标识符Unsafe类中的获取了三种CAS转换如下,这三种都是native新方法,都是Hotspot标识符,
上头用compareAndSwapInt新方法来举例说明
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);新方法参数:
1、普通人模板2、字符串缓可知最大值的倍数(根据普通人模板和倍数就可以给予完全一致的表达式)3、期望普通人模板中的的原最大值4、字符串预览最大值
1-4-2、Hotspot层1-4-2-1、Unsafe_CompareAndSwapInt新方法线程Hotspot新方法源码如下:
#unsafe.cppUNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))UnsafeWrapper("Unsafe_CompareAndSwapInt");oop p = JNIHandles::resolve(obj);// 根据倍数,测算value的重定向jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);// Atomic::cmpxchg(x, addr, e) cas逻辑上 x:要转换的最大值 e:要更为的最大值//cas急于,来到期望最大值e,总和e,此新方法来到true //cas惨败,来到缓可知中的的value最大值,不总和e,此新方法来到falsereturn (jint)(Atomic::cmpxchg(x, addr, e)) == e;1-4-2-1-1、新方法给定首再行线程的新方法:
Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)
前面两个最大值是hotspot传布的最大值,后面四个最大值为java新方法传布刚才的。
其次根据倍数,测算value的重定向
//给予普通人oop p = JNIHandles::resolve(obj); // 根据倍数,测算value的重定向 jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);然后线程Atomic::cmpxchg借助CAS转换
// Atomic::cmpxchg(x, addr, e) cas逻辑上 x:要转换的最大值 e:要更为的最大值//cas急于,来到期望最大值e,总和e,此新方法来到true //cas惨败,来到缓可知中的的value最大值,不总和e,此新方法来到falsereturn (jint)(Atomic::cmpxchg(x, addr, e)) == e;1-4-2-2、Atomic::cmpxchg新方法无需注意到上头的标识符是Linux_x86,多种不同系统处理CAS是多种不同的
#atomic_linux_x86.inline.hppinline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) {//说明局限性可执行周边环境前提为虚拟化周边环境int mp = os::is_MP();//LOCK_IF_MP(%4) 在虚拟化周边环境下,为 cmpxchgl 命令掺入 lock 前缀,以达到缓可知屏障的效果//cmpxchgl 命令是构成在 x86 虚拟化及 IA-64 虚拟化中的的一个水分子条件命令,//它则会首再行更为 dest 指针抛出的缓可知最大值前提和 compare_value 的最大值小于,//如果小于,则双向转换 dest 与 exchange_value,否则就单上都地将 dest 抛出的缓可知最大值交给exchange_value。//这条命令启动了整个 CAS 转换,因此它也被称做 CAS 命令。originallyasmoriginally 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;cmpxchgl的详尽可执行流程:
首再行,输入是"r" (exchange_value), “a” (compare_value), “r” (dest), “r” (mp),声称compare_value可知入eax字节,而exchange_value、dest、mp的最大值可知入任意的通用字节。嵌入式汇编规定把控制器和输入字节按统一时序编号,时序是从控制器字节序列从左到右一个大以“%0”开始,分别记为%0、%1···%9。反之亦然,控制器的eax是%0,输入的exchange_value、compare_value、dest、mp分别是%1、%2、%3、%4。
因此,cmpxchg %1,(%3)其实声称cmpxchg exchange_value,(dest)
无需注意到的是cmpxchg有个比如说转换数eax,其仅仅流程是再行更为eax的最大值(也就是compare_value)和dest重定向所可知的最大值前提小于,
控制器是"=a" (exchange_value),声称把eax中的可知的最大值可知储exchange_value表达式中的。
Atomic::cmpxchg这个数组再次来到最大值是exchange_value,反之亦然,如果cmpxchgl可执行时compare_value和dest指针抛出缓可知最大值小于则则会使得dest指针抛出缓可知最大值变为exchange_value,再次eax可知的compare_value赋最大值给了exchange_value表达式,即数组再次来到的最大值是原再行的compare_value。此时Unsafe_CompareAndSwapInt的来到最大值(jint)(Atomic::cmpxchg(x, addr, e)) == e就是true,说明CAS急于。如果cmpxchgl可执行时compare_value和(dest)多达则则会把局限性dest指针抛出缓可知的最大值可知储eax,再次控制器时赋最大值给exchange_value表达式作为来到最大值,避免(jint)(Atomic::cmpxchg(x, addr, e)) == e得到false,说明CAS惨败。
传统处理器命令集虚拟化基本上都则会获取 CAS 命令,例如 x86 和 IA-64 虚拟化中的的 cmpxchgl 命令和 comxchgq 命令,sparc 虚拟化中的的 cas 命令和 casx 命令。
不管是 Hotspot 中的的 Atomic::cmpxchg 新方法,还是 Java 中的的 compareAndSwapInt 新方法,它们其本质上都是对确切来说游戏平台的 CAS 命令的一层简单封装。CAS 命令作为一种硬件内联,有着天然的水分子性,这也正是 CAS 的价最大值所在。
1-5、通过CAS借助针我们再离开一开始的标识符中的,该如何借助针呢
public class ThreadLockCAS { private volatile static int count = 0; public static void main(String[] args) { for (int i = 0; i < 10; i++) { Thread thread = new Thread(new Runnable() { @Override public void run() { for (int j = 0; j < 10000; j++) { count++; } } }); thread.start(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("count:" + count); }}1-5-1、借助思路通过上对CAS的了解,我们就可以这么转换:
1、设置一个中的间最大值为02、第一个线程刚才的时候通过CAS将中的间最大值改名13、其他线程刚才 其后将要将0改名1的时候就则会惨败4、第一个线程业务转换完毕,其后通过cas将中的间最大值改名05、下一个线程其后通过CAS将0改名1就则会急于,给予针
1-5-2、借助一个CAS更为与转换的借助类1-5-2-1、借助加针的类其中的UnsafeFactory类的标识符在纸片之前贴过,这个地方就暂时贴了。这个类中的主要就是cas()这个新方法。这就是CAS模式加针的转换。
State就是作为变动转换的最大值。
public class CASLock { private volatile int state; private static final Unsafe UNSAFE; private static final long OFFSET; static { try { UNSAFE= UnsafeFactory.getUnsafe(); OFFSET=UnsafeFactory.getFieldOffset(UNSAFE,CASLock.class,"state"); } catch (Exception e) { throw new Error(e); } } //设置CAS转换 public boolean cas(){ return UNSAFE.compareAndSwapInt(this,OFFSET,0,1); } public int getState() { return state; } public void setState(int state) { this.state = state; }}1-5-2-2、常用针借助如下标识符,在第一个for反转中的创始人线程,然后线程中的借助了一个电磁场转换,这样第一个线程进到的针在此之后,其他线程都在透过电磁场转换,等第一个线程通过casLock.setState(0),拘禁针的时候,下一个线程casLock.getState()==0 && casLock.cas()就可以正式成立。
public class ThreadLockCAS { private volatile static int count = 0; static CASLock casLock = new CASLock(); public static void main(String[] args) { for (int i = 0; i < 10; i++) { Thread thread = new Thread(() -> { for (; ; ) { //state=0 if (casLock.getState() == 0 && casLock.cas()) { try { for (int j = 0; j < 10000; j++) { count++; } } finally { casLock.setState(0); } break; } } }); thread.start(); } try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(count); }}虽然通过以上标识符借助了加针转换,由于常用了电磁场转换,这样下次的线程就则会直至空转,消耗CPU资源,毕竟这样得借助模式是不太友好的
1-6、CAS缺失CAS 虽然高效地化解了水分子转换,但是还是普遍可知在一些缺失的,主要表现在三个上都:
电磁场 CAS 长时间地不急于,则则会给 CPU 造就非常大的开销只能确保一个共享表达式水分子转换ABA 原因1-6-1、ABA原因及化解方案CAS搜索算法借助一个重要前提无需取出缓可知中的某时刻的统计数据,而在下时刻更为并代替,那么在这个时间差类则会避免统计数据的转变。
1-6-1-1、什么是ABA原因人口为120人多个线程对一个水分子类透过转换的时候,某个线程在而会内将水分子类的最大值A重写为B,又马上将其重写为A,此时其他线程不感受,还是则会重写急于。
测试ABA原因
标识符运行流程:第一个线程要从1改名3,进到线程在此之后下次1秒。第二个线程将1改名2,然后又将2改名1第一个线程从1改名3,(因为原最大值还是1,这样就重写急于了)-其实此1非彼1
public class ABATest { public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(1); new Thread(() -> { int value = atomicInteger.get(); System.out.println("Thread1 read value: " + value); // 阻塞1s LockSupport.parkNanos(1000000000L); // Thread1通过CAS重写value最大值为3 if (atomicInteger.compareAndSet(value, 3)) { System.out.println("Thread1 update from " + value + " to 3"); } else { System.out.println("Thread1 update fail!"); } }, "Thread1").start(); new Thread(() -> { int value = atomicInteger.get(); System.out.println("Thread2 read value: " + value); // Thread2通过CAS重写value最大值为2 if (atomicInteger.compareAndSet(value, 2)) { System.out.println("Thread2 update from " + value + " to 2"); // do something value = atomicInteger.get(); System.out.println("Thread2 read value: " + value); // Thread2通过CAS重写value最大值为1 if (atomicInteger.compareAndSet(value, 1)) { System.out.println("Thread2 update from " + value + " to 1"); } } }, "Thread2").start(); }}运行结果如下:
Thread1不似乎Thread2对value的转换,误以为value=1不能重写过
1-6-1-2、ABA原因的化解方案统计文档有个针称做急切针,是一种基于统计数据正式版借助统计数据同步的有助于,每次重写一次统计数据,正式版就则会透过累加。
AtomicStampedReference化解CAS的ABA原因同样,Java也获取了确切来说的水分子所述类AtomicStampedReference
reference即我们仅仅传输的表达式,stamp是正式版,每次重写可以通过+1确保正式版所有基。这样就可以确保每次重写后的正式版也则会往上递增。
AtomicMarkableReference化解CAS的ABA原因可以了解为纸片AtomicStampedReference的简化版,就是不体谅重写过几次,仅有仅有体谅前提重写过。因此表达式mark是boolean各种类型,仅有记录最大值前提有过重写。
常用AtomicStampedReference,依靠正式版号化解ABA原因public class AtomicStampedReferenceTest { public static void main(String[] args) { // 假设AtomicStampedReference Pair.reference最大值为1, Pair.stamp为1 AtomicStampedReference atomicStampedReference = new AtomicStampedReference(1,1); new Thread(()->{ int[] stampHolder = new int[1]; int value = (int) atomicStampedReference.get(stampHolder); int stamp = stampHolder[0]; System.out.println("Thread1 read value: " + value + ", stamp: " + stamp); // 阻塞1s LockSupport.parkNanos(1000000000L); // Thread1通过CAS重写value最大值为3 stamp是正式版,每次重写可以通过+1确保正式版所有基 if (atomicStampedReference.compareAndSet(value, 3,stamp,stamp+1)) { System.out.println("Thread1 update from " + value + " to 3"); } else { System.out.println("Thread1 update fail!"); } },"Thread1").start(); new Thread(()->{ int[] stampHolder = new int[1]; int value = (int)atomicStampedReference.get(stampHolder); int stamp = stampHolder[0]; System.out.println("Thread2 read value: " + value+ ", stamp: " + stamp); // Thread2通过CAS重写value最大值为2 if (atomicStampedReference.compareAndSet(value, 2,stamp,stamp+1)) { System.out.println("Thread2 update from " + value + " to 2"); // do something value = (int) atomicStampedReference.get(stampHolder); stamp = stampHolder[0]; System.out.println("Thread2 read value: " + value+ ", stamp: " + stamp); // Thread2通过CAS重写value最大值为1 if (atomicStampedReference.compareAndSet(value, 1,stamp,stamp+1)) { System.out.println("Thread2 update from " + value + " to 1"); } } },"Thread2").start(); }}可执行结果
总结:
本篇文章主要描写CAS的加针的借助模式,以及通过常用AtomicStampedRefrence和AtomicMarkableReference化解ABA的原因
作者:Jony_zhang链接:
。英特达泊西汀片(60mg)能治什么疾病肠道菌群失调怎么调理
利活牌乳酸菌素片
水土不服拉肚子
胃不舒服怎么快速缓解
- 05-20英特尔宣告以54亿美元现金收购以色列半导体公司Tower Semiconductor
- 05-20转会风波将影响徐新状态?未来中日之战,他将暂时锁定首发出战
- 05-20Unity揭晓自研游戏《Gigaya》,15人开发,资源代码将全免费开放
- 05-20公务员应考之定义判断之如何判断意图
- 05-20小天离队后对前队友全胜,刘青松:真不想让他拿好成绩,拿了又要叫
- 05-20孩子进入青春期叛逆期,亲子沟通变为难题,父母要巧用“刺猬定律”
- 05-20家里养多肉,适当虐一虐,反而长得隆,叶子肥又厚
- 05-20如果你想跟乔纳森和谐相处,就要做到下面5件事情
- 05-20雪松200亿涉众募资调查:假借灰色通道、裹挟一众伪央企 底层资产涉“空转”贸易
- 05-20甘肃一小狗练就绝技每天到肉摊“拍照”,站直身子用眼神不停暗示