JUC中有一个常用但是又不常用的一组类,经常以Atomic开头,我们叫做原子类,原子类的作用在用到的时候作用很大,不需要的时候,也可以说很少用。但是作为一个Java程序员,还是有必要去解读一下原子类的源码。
目录
什么是原子类
原子表示不能再拆的东西,表示是最小的东西,在生物或者化学里面,原子是最小单位了,不能拆分。在系统中,原子也是同样的表示,原子操作表示,这一系列操作不能分开,这东西都有CPU进行处理,CPU知道这些东西是否是原子的。
JDK和原子类
1.5之前都是使用synchronize。
1.5之后在JUC包中引入了Atomic。
原子类所有的类都实现了Serializable接口,表示可以被序列化。
原子类的分类
普通类型
AtomicBoolean
AtomicInteger
AtomicLong
AtomicReference
数组类型
AtomicLongArray
AtomicIntegerArray
AtomicReferenceArray
字段类型
AtomicLongFieldUpdater
AtomicIntegerFieldUpdater
AtomicReferenceFieldUpdater
版本引用类型
AtomicStampedReference
AtomicMarkableReference
累加类型
DoubleAccumulator
DoubleAdder
LongAccumulator
LongAdder
源码
大伙看一下源码可知,所有的原子类内部结构极其相似,我们拿AtomicInteger来举例。
主变量为volatile类型的int,这是这个原子类所存的值。
构造函数为,一个有参,一个无参。
创建一个偏移量,在堆中找到此原子的偏移量,为了在CAS时可以拿到内存的数据进行比较。
创建本地方法的对象。
基本常用的方法为get、set、compareAndSet、getAndIncrement等。
最基本的问题为:CAS原理、ABA问题、解决ABA问题。
原理
在类中,所有的操作均为原子操作,除了普通的get和set,其余更新等操作,归根结底,最终都调用了本地方法,本地方法CAS,更新。
CAS的步骤:
通过偏移量获取内存中的值。
根据内存的值和期望的值比较。
如果一样,则替换。
不一样,(死循环或者直接返回)。
通过AtomicStampedReference、AtomicMarkableReference这两个类可以解决ABA,是因为这两个类,都有一个标识,这个标识就是关键。
这两个类也有区别的,Stamped的标识是一个int类型的值,Markable的标识是一个boolean值。
他们在进行CAS的时候,不光比较内存的值和预期值,还会看版本号,查看版本号和预期值都通过,才会进行替换。
CAS的死循环怎么退出的?在1.8中并没有发现死循环,但是死循环是怎么出来的呢,卧槽,不会一直在循环比较吧,万一值回不去了呢,怎么办。
实现
使用基本类型,全部都是原子操作,而且都是单一操作。boolean实现转成Integer后进行CAS。
数组类型在进行操作时,方法的参数加了一个索引。
引用类型,提供get和set方法,替换对象方法,通过get可以得到方法的对象。
字段类型,和引用类像似,在初始化的时候指定对象中的某个字段。
介绍原子类的用法,方腾飞的文章:http://ifeve.com/java-atomic/
没有贴代码,没有过多的详解,因为大伙看源码真的可以看出来其中的门道,不足之处还请大家多多指点。今天情绪不稳定,可能做出来的笔记质量也很差。