原子类示例其实对于简单的原子问题是有无锁解决方案的。JavaSDK并发包在封装和提炼这个无锁方案后,实现了一系列的原子类。下面我们就来看看如何使用原子类来实现一个线程安全的累加器,让你对原子类有一个初步的了解。公共类测试{AtomicLongcount=newAtomicLong(0);voidadd10K(){intidx=0;while(idx++<10000){count.getAndIncrement();}}}用一个原子类AtomicLong替换原来的long变量count,把原来的count+=1替换为count.getAndIncrement(),只需要简单的做这两个改动就可以让add10K()方法线程安全了。与互斥锁方案相比,无锁方案最大的优势在于性能。为了保证互斥,互斥锁方案需要进行加锁和解锁操作,但是加锁和解锁操作本身就很消耗性能;同时,得不到锁的线程会进入阻塞状态,从而触发线程切换。性能消耗也很大。无锁方案的实现原理cas指令对于原子类的高性能来说其实很简单,只有硬件支持:为了解决并发问题,CPU提供了CAS指令(CAS,全称是CompareAndSwap,即“比较和交换”)。CAS指令包含3个参数:共享变量的内存地址A、用于比较的值B、共享变量的新值C;并且只有当内存中地址A处的值等于B时,内存中地址A处的值才能更新为新值C。作为CPU指令,CAS指令本身可以保证原子性.您可以通过下面CAS指令的模拟代码来了解CAS的工作原理。在下面的仿真程序中,有两个参数,一个是期望值expect,一个是需要写入的新值newValue。只有当count的当前值等于期望值expect时,count才会更新为newValue。classSimulatedCAS{//CAS的模拟实现,仅用于帮助理解int计数;synchronizedintcas(intexpect,intnewValue){intcurValue=count;//读取count的当前值if(curValue==expect){//比较当前count值是否==期望值count=newValue;//如果是,则更新count的值}returncurValue;//写入前返回值}}使用CAS解决并发问题,通常会伴随自旋,所谓自旋其实就是一种循环尝试。因为CAS的执行不一定成功,如果失败了,需要循环重试。ABA问题在CAS程序中,有一个问题可能经常被大家忽略,那就是ABA的问题。什么是ABA问题?假设我们更新计数的值。Count本来就是A,在线程T1读取count之后更新count之前,count可能会被线程T2更新为B,再被T3更新回A。这样,虽然线程T1看到的一直是A,但是已经被其他线程更新了。这就是ABA问题。可能大多数情况下我们不关心ABA问题,比如值的原子自增,但我们不可能在所有情况下都关心。比如原子更新对象可能需要关心ABA的问题,因为虽然两个A是相等的,但是第二个A的属性可能已经改变了。getAndIncrement()源码分析原子类AtomicLong的getAndIncrement()方法内部是基于CAS实现的。在Java1.8中,getAndIncrement()方法调用unsafe.getAndAddLong()方法。这里通过this和valueOffset这两个参数可以唯一确定共享变量的内存地址。finallonggetAndIncrement(){returnunsafe.getAndAddLong(this,valueOffset,1L);}unsafe.getAndAddLong()方法源码如下。该方法首先读取内存中共享变量的值,然后循环调用compareAndSwapLong()方法尝试设置共享变量的值,直到成功。publicfinallonggetAndAddLong(Objecto,longoffset,longdelta){longv;do{v=getLongVolatile(o,offset);//读取内存中的值}while(!compareAndSwapLong(o,offset,v,v+delta));returnv;}//原子更新变量为x//条件是内存中的值等于expected//返回truenativebooleancompareAndSwapLong(Objecto,longoffset,longexpected,longx如果更新成功);compareAndSwapLong()是native方法,只有当内存中共享变量的值等于预期时,才会将共享变量的值更新为x并返回true;否则返回fasle。compareAndSwapLong和CAS指令在语义上的区别仅在于返回值。getAndAddLong()方法的实现基本上是CAS使用的经典示例。做{oldV=xxxx;//获取当前值newV=...oldV...//根据当前值计算新值}while(!compareAndSet(oldV,newV);原子类概述JavaSDK并发包中提供的原子类内容非常丰富,我们可以将它们分为五类:原子化基本数据类型原子化对象引用类型原子化数组原子化对象属性更新器原子化累加器。1、原子化相关的基本数据类型有AtomicBoolean、AtomicInteger、AtomicLong。提供的方法主要有以下几种。具体可以参考SDK源码/当前值+=delta,返回之前的值+=addAndGet(delta)//当前值+=delta,返回之后的值+=compareAndSet(expect,update)//CAS操作,返回是否成功//以下四种方法,传入func函数getAndUpdate(func)updateAndGet(func)getAndAccumulate(x,func)accumulateAndGet(x,func)即可计算新值2.原子对象引用类型相关实现包括AtomicReference、AtomicStampedReference和AtomicMarkableReference,可用于实现对象引用的原子更新。AtomicReference提供的方法与原子基本数据类型类似,这里不再赘述。但是需要注意的是,对象引用的更新需要关注ABA问题。AtomicStampedReference和AtomicMarkableReference这两个原子类可以解决ABA问题。解决ABA问题的思路其实很简单,就是加一个版本号维度。AtomicStampedReference实现的CAS方法增加了一个版本号参数。方法签名如下:booleancompareAndSet(VexpectedReference,VnewReference,intexpectedStamp,intnewStamp)AtomicMarkableReference的实现机制更简单,将版本号简化为布尔值。签名如下:booleancompareAndSet(VexpectedReference,VnewReference,booleanexpectedMark,booleannewMark)3.AtomicIntegerArray、AtomicLongArray和AtomicReferenceArray与原子数组的实现有关。使用这些原子类,我们可以原子地更新数组中的每个元素。这些类提供的方法与原子基本数据类型的区别只是每个方法多了一个数组索引参数,这里不再赘述。4、原子对象属性更新器相关实现包括AtomicIntegerFieldUpdater、AtomicLongFieldUpdater和AtomicReferenceFieldUpdater,可用于原子地更新对象的属性。这三个方法都是使用反射机制实现的。创建updater的方法如下:publicstaticAtomicXXXFieldUpdaternewUpdater(Classtclass,StringfieldName)需要注意的是对象属性必须是volatile类型,只有这样才能能见度得到保证;如果对象属性不是volatile类型,newUpdater()方法将抛出运行时异常IllegalArgumentException。newUpdater()的方法参数只有类信息,没有对象引用,但是更新对象的属性必须需要对象引用,所以在原子操作的方法参数中传入对象引用。与原子基本数据类型相比,原子对象属性更新器相关的方法只是多了一个对象引用参数。例如原子操作compareAndSet()相对于原子基本数据类型多了一个对象引用obj。booleancompareAndSet(Tobj,intexpect,intupdate)优点:与原子化的基本数据类型相比,原子化的对象属性更新器只需要在抽象父类中声明一个静态更新器,就可以在.在会为其所属类创建大量实例对象的情况下,使用AtomicXXXFieldUpdater可以节省内存开销。5.原子累加器DoubleAccumulator、DoubleAdder、LongAccumulator、LongAdder仅用于累加操作。与原子基本数据类型相比,速度更快,但不支持compareAndSet()方法。如果只需要累加操作,用原子累加器会更好。//LongAdder的初值固定为0LongAdderlongAdder=newLongAdder();longAdder.add(2);longAdder.increment();//LongAccumulator需要设置初值和累加规则LongAccumulatorlongAccumulator=newLongAccumulator((x,y)->x-y,3);longAccumulator.accumulate(2);原子类:无锁实用程序类的一个很好的例子-GeekTimeBeenUsingAtomicInteger?尝试FieldUpdater
