1.Go并发编程之原子操作sync/atomic
2.太强了!源码阿里老哥分享的源码JDK源码学习指南,含8大核心内容讲解
3.C++ atomic详解
4.为什么会有 AtomicReference ?
5.深入学习CAS底层原理
Go并发编程之原子操作sync/atomic
Go语言的并发编程中,sync/atomic包提供了底层的源码原子内存操作,用于处理并发环境中的源码数据同步和冲突避免。这个包利用了CPU的源码买卖强度指标公式源码原子操作指令,确保在并发情况下,源码对变量的源码操作是线程安全的。然而,源码官方建议仅在必要且确实涉及底层操作时使用,源码如避免使用channel或sync包中的源码锁的场景。
sync/atomic包的源码核心是5种基本数据类型的原子操作:add(只支持int、int、源码uint、源码uint和uintptr),源码以及一个扩展的Value类型,后者在1.4版本后支持Load、Store、CompareAndSwap和Swap方法,可用于操作任意类型的数据。Value类型尤其重要,因为它扩展了原子操作的适用范围。
具体来说,swap操作(如SwapInt)用于原子地替换内存中的PHP简单聊天源码值,compare-and-swap(CAS)则检查并替换值,如果当前值与预期值一致。add操作(如AddInt)则进行加法操作并返回新值,而load、store操作分别用于读取和写入值,如LoadInt和StoreInt。
在实际使用时,例如对map的并发读写,可以通过Value类型避免加锁。sync/atomic的相关源码和示例可在GitHub的教程[1]和作者的个人网站[2]中找到。至于进一步学习,可以关注公众号coding进阶获取更多资源,或者在知乎[3]上查找无忌的资料。
参考资料:
[1] Go语言初级、中级和高级教程: github.com/jincheng9/go...
[2] Jincheng's Blog: jincheng9.github.io/
[3] 无忌: zhihu.com/people/thucuh...
太强了!阿里老哥分享的JDK源码学习指南,含8大核心内容讲解
Java开发中,JDK源码的重要性不言而喻。作为Java运行环境的基石,JDK涵盖了Java的全部运行环境和开发工具,没有它,程序编译都无从谈起。为此,机读卡阅卷系统源码本文将分享一份来自阿里的资深程序员整理的JDK源码学习指南。
这份指南详尽介绍了JDK源码的多个核心内容,包括多线程基础、Atomic类、Lock与Condition接口、同步工具类、并发容器、线程池与Future、ForkJoinPool分治算法、异步编程工具CompletableFuture等。需要这份资料的朋友,请点击此处获取完整版。
以下是学习指南的具体章节:
第1章 多线程基础
第2章 Atomic类
第3章 Lock与Condition
第4章 同步工具类
第5章 并发容器
第6章 线程池与Future
第7章 ForkJoinPool
第8章 CompletableFuture
以上就是这份JDK源码学习笔记的概述,感兴趣的朋友可以点击此处获取完整版资料。
C++ atomic详解
C++中的原子操作是为了确保在多核环境下,对共享数据的读写操作不会出现竞态条件,保证数据的一致性。理解原子操作的底层实现至关重要,但网上资源往往缺乏详细讲解编译器如何实现的细节。本文旨在填补这一空白,深入解析原子操作的技术和实现方法,主要参考了《深入理解 linux内核》和gcc源码。
原子操作通常依赖于某些具有读-修改-写性质的源码简单游文件汇编指令,如x平台上的xadd。这些指令确保操作以单个指令执行,避免其他CPU的干扰,从而创建了原子操作。然而,即使有了原子指令,创建临界区和优化内存访问仍需额外的内存屏障和优化屏障机制。
gcc编译器内部实现了多种原子操作,例如__atomic_load_n和__atomic_fetch_op_N,这些操作在libatomic库中提供了一致的接口。在libatomic_i.h中定义了宏,如__atomic_load,根据需要链接到相应的c文件,如load_n文件。底层实现可能有多种,如基于内存屏障的__atomic_load提供了不同实现。
在优化和内存屏障方面,编译器可能会重新排列指令以提高执行效率,但处理同步时必须保持指令的执行顺序,避免指令重排序导致的问题。内存屏障原语确保操作的顺序性,而优化屏障则防止编译器混淆操作前后的内容。
总的dfa最小化源码来说,理解和掌握C++的原子操作及其底层实现,对于编写并发程序至关重要,尤其是在处理多核环境下的数据同步和一致性保障。
为什么会有 AtomicReference ?
原子性工具类AtomicReference是Java.util.concurrent.atomic包下的一个类,它能够确保在修改对象引用时的线程安全性。例如在处理账户问题时,多个线程可能同时向同一个账户存入款项,使用AtomicReference可以避免这种情况导致的数据不一致问题。
在使用AtomicReference时,我们需要了解它的基本使用方法。首先声明一个全局变量BankCard,并使用volatile关键字对其进行修饰,以确保在对其引用进行变化后对其他线程可见。然后,我们可以通过AtomicReference来封装BankCard的引用,使用get()方法获得原子性的引用,接着使用CAS(Compare and Swap)乐观锁进行非阻塞更新。这样可以确保在修改引用时的线程安全性。
AtomicReference源码解析中,我们发现它主要依赖于sun.misc.Unsafe类的native方法来保证操作的原子性。Unsafe的objectFieldOffset方法可以获取成员属性在内存中的地址相对于对象内存地址的偏移量,这个偏移量就是valueOffset,方便后续通过内存地址直接进行操作。value是AtomicReference的实际值,由于使用了volatile,这个值实际上就是内存值。
AtomicReference与AtomicInteger非常相似,它们内部都使用了Unsafe、value、valueOffset等属性。get()和set()方法分别可以原子性地读取和设置AtomicReference中的数据。lazySet方法则在没有内存屏障的情况下读写变量,以减少开销。getAndSet方法则调用unsafe中的getAndSetObject方法,涉及getObjectVolatile和compareAndSwapObject方法,它们在do...while循环中,每次获取最新对象引用的值,如果使用CAS成功交换两个对象,则直接返回更新前的内存值,即旧值。
AtomicReference的关键方法CAS(Compare and Swap)在compareAndSet方法中实现,与AtomicInteger不同的是,AtomicReference调用compareAndSwapObject方法。这段代码底层使用了Atomic:cmpxchg方法进行CAS交换,并将旧值进行decode返回。
weakCompareAndSet方法在JDK1.8中与compareAndSet方法完全相同,但实际上这是JDK源码设计的巧妙之处,用于处理特定场景下的线程安全问题。
在使用AtomicReference时,我们需要充分了解它的特性和源码实现,以确保在多线程环境下正确地管理和更新对象引用。本文主要介绍了AtomicReference的出现背景、使用场景以及源码分析,涵盖了网络上关于AtomicReference的大部分内容。
深入学习CAS底层原理
什么是CAS
CAS是Compare-And-Swap的缩写,意思为比较并交换。以AtomicInteger为例,其提供了compareAndSet(intexpect,intupdate)方法,expect为期望值(被修改的值在主内存中的期望值),update为修改后的值。compareAndSet方法返回值类型为布尔类型,修改成功则返回true,修改失败返回false。
举个compareAndSet方法的例子:
publicclassAtomticIntegerTest{ publicstaticvoidmain(String[]args){ AtomicIntegeratomicInteger=newAtomicInteger(0);booleanresult=atomicInteger.compareAndSet(0,1);System.out.println(result);System.out.println(atomicInteger.get());}}上面例子中,通过AtomicInteger(intinitialValue)构造方法指定了AtomicInteger类成员变量value的初始值为0:
publicclassAtomicIntegerextendsNumberimplementsjava.io.Serializable{ ......privatevolatileintvalue;/***CreatesanewAtomicIntegerwiththegiveninitialvalue.**@paraminitialValuetheinitialvalue*/publicAtomicInteger(intinitialValue){ value=initialValue;}......}接着执行compareAndSet方法,main线程从主内存中拷贝了value的副本到工作线程,值为0,并将这个值修改为1。如果此时主内存中value的值还是为0的话(言外之意就是没有被其他线程修改过),则将修改后的副本值刷回主内存更新value的值。所以上面的例子运行结果应该是true和1:
将上面的例子修改为:
publicclassAtomticIntegerTest{ publicstaticvoidmain(String[]args){ AtomicIntegeratomicInteger=newAtomicInteger(0);booleanfirstResult=atomicInteger.compareAndSet(0,1);booleansecondResult=atomicInteger.compareAndSet(0,1);System.out.println(firstResult);System.out.println(secondResult);System.out.println(atomicInteger.get());}}上面例子中,main线程第二次调用compareAndSet方法的时候,value的值已经被修改为1了,不符合其expect的值,所以修改将失败。上面例子输出如下:
CAS底层原理查看compareAndSet方法源码:
/***Atomicallysetsthevalueto{ @codenewValue}*ifthecurrentvalue{ @code==expectedValue},*withmemoryeffectsasspecifiedby{ @linkVarHandle#compareAndSet}.**@paramexpectedValuetheexpectedvalue*@paramnewValuethenewvalue*@return{ @codetrue}ifsuccessful.Falsereturnindicatesthat*theactualvaluewasnotequaltotheexpectedvalue.*/publicfinalbooleancompareAndSet(intexpectedValue,intnewValue){ returnU.compareAndSetInt(this,VALUE,expectedValue,newValue);}该方法通过调用unsafe类的compareAndSwapInt方法实现相关功能。compareAndSwapInt方法包含四个参数:
this,当前对象;
valueOffset,value成员变量的内存偏移量(也就是内存地址):
privatestaticfinallongvalueOffset;static{ try{ valueOffset=unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));}catch(Exceptionex){ thrownewError(ex);}}expect,期待值;
update,更新值。
所以这个方法的含义为:获取当前对象value成员变量在主内存中的值,和传入的期待值相比,如果相等则说明这个值没有被别的线程修改过,然后将其修改为更新值。
那么unsafe又是什么?它的compareAndSwapInt方法是原子性的么?查看该方法的源码:
/***AtomicallyupdatesJavavariableto{ @codex}ifitiscurrently*holding{ @codeexpected}.**<p>Thisoperationhasmemorysemanticsofa{ @codevolatile}read*andwrite.CorrespondstoCatomic_compare_exchange_strong.**@return{ @codetrue}ifsuccessful*/@HotSpotIntrinsicCandidatepublicfinalnativebooleancompareAndSetInt(Objecto,longoffset,intexpected,intx);该方法并没有具体Java代码实现,方法通过native关键字修饰。由于Java方法无法直接访问底层系统,Unsafe类相当于一个后门,可以通过该类的方法直接操作特定内存的数据。Unsafe类存在于sun.msic包中,JVM会帮我们实现出相应的汇编指令。Unsafe类中的CAS方法是一条CPU并发原语,由若干条指令组成,用于完成某个功能的一个过程。原语的执行必须是连续的,在执行过程中不允许被中断,不会存在数据不一致的问题。
getAndIncrement方法剖析了解了CAS原理后,我们回头看下AtomicInteger的getAndIncrement方法源码:
/***Atomicallyincrementsthecurrentvalue,*withmemoryeffectsasspecifiedby{ @linkVarHandle#getAndAdd}.**<p>Equivalentto{ @codegetAndAdd(1)}.**@returnthepreviousvalue*/publicfinalintgetAndIncrement(){ returnU.getAndAddInt(this,VALUE,1);}该方法通过调用unsafe类的getAndAddInt方法实现相关功能。继续查看getAndAddInt方法的源码:
/***Atomicallyaddsthegivenvaluetothecurrentvalueofafield*orarrayelementwithinthegivenobject{ @codeo}*atthegiven{ @codeoffset}.**@paramoobject/arraytoupdatethefield/elementin*@paramoffsetfield/elementoffset*@paramdeltathevaluetoadd*@returnthepreviousvalue*@since1.8*/@HotSpotIntrinsicCandidatepublicfinalintgetAndAddInt(Objecto,longoffset,intdelta){ intv;do{ v=getIntVolatile(o,offset);}while(!weakCompareAndSetInt(o,offset,v,v+delta));returnv;}结合源码,我们便可以很直观地看出为什么AtomicInteger的getAndIncrement方法是线程安全的了:
o是AtomicInteger对象本身;offset是AtomicInteger对象的成员变量value的内存地址;delta是需要变更的数量;v是通过unsafe的getIntVolatile方法获得AtomicInteger对象的成员变量value在主内存中的值。dowhile循环中的逻辑为:用当前对象的值和var5比较,如果相同,说明该值没有被别的线程修改过,更新为v+delta,并返回true(CAS);否则继续获取值并比较,直到更新完成。
CAS的缺点CAS并不是完美的,其存在以下这些缺点:
如果刚好while里的CAS操作一直不成功,那么对CPU的开销大;
只能确保一个共享变量的原子操作;
存在ABA问题。
CAS实现的一个重要前提是需要取出某一时刻的数据并在当下时刻比较交换,这之间的时间差会导致数据的变化。比如:thread1线程从主内存中取出了变量a的值为A,thread2页从主内存中取出了变量a的值为A。由于线程调度的不确定性,这时候thread1可能被短暂挂起了,thread2进行了一些操作将值修改为了B,然后又进行了一些操作将值修改回了A,这时候当thread1重新获取CPU时间片重新执行CAS操作时,会发现变量a在主内存中的值仍然是A,所以CAS操作成功。
解决ABA问题那么如何解决CAS的ABA问题呢?由上面的阐述课件,光通过判断值是否相等并不能确保在一定时间差内值没有变更过,所以我们需要一个额外的指标来辅助判断,类似于时间戳,版本号等。
JUC为我们提供了一个AtomicStampedReference类,通过查看它的构造方法就可以看出,除了指定初始值外,还需指定一个版本号(戳):
/***Createsanew{ @codeAtomicStampedReference}withthegiven*initialvalues.**@paraminitialReftheinitialreference*@paraminitialStamptheinitialstamp*/publicAtomicStampedReference(VinitialRef,intinitialStamp){ pair=Pair.of(initialRef,initialStamp);}我们就用这个类来解决ABA问题,首先模拟一个ABA问题场景:
publicclassAtomticIntegerTest{ publicstaticvoidmain(String[]args){ AtomicReference<String>atomicReference=newAtomicReference<>("A");newThread(()->{ //模拟一次ABA操作atomicReference.compareAndSet("A","B");atomicReference.compareAndSet("B","A");System.out.println(Thread.currentThread().getName()+"线程完成了一次ABA操作");},"thread1").start();newThread(()->{ //让thread2先睡眠2秒钟,确保thread1的ABA操作完成try{ TimeUnit.SECONDS.sleep(2);}catch(InterruptedExceptione){ e.printStackTrace();}booleanresult=atomicReference.compareAndSet("A","B");if(result){ System.out.println(Thread.currentThread().getName()+"线程修改值成功,当前值为:"+atomicReference.get());}},"thread2").start();}}运行程序,输出如下:
使用AtomicStampedReference解决ABA问题:
publicclassAtomicIntegerextendsNumberimplementsjava.io.Serializable{ ......privatevolatileintvalue;/***CreatesanewAtomicIntegerwiththegiveninitialvalue.**@paraminitialValuetheinitialvalue*/publicAtomicInteger(intinitialValue){ value=initialValue;}......}0程序输出如下:
2024-12-24 20:32
2024-12-24 19:51
2024-12-24 19:50
2024-12-24 19:26
2024-12-24 19:21
2024-12-24 19:02
2024-12-24 18:20
2024-12-24 18:02