原子性说明
无论存在多少个子操作,只要将这部分操作通过加锁的形式进行锁定,那么就可以视为一个原子性操作。
原子性操作 <=> 线程安全。
原子操作类概述
原子操作类:提供了可以保证线程安全的类,可以直接使用。
当程序更新一个变量时,如果多线程同时更新这个变量,可能得到期望之外的值,比如变量i=1
,A线程更新i + 1
,B线程也更新i + 1
,经过两个线程操作之后可能i不等于3,而是等于2。
解决方案:
- 加锁
- 使用
volatile
修饰变量i - 使用原子操作类
JDK5开始提供了java.util.concurrent.atomic
包,这个包中的原子操作类提供了一种用法简单、性能高效(CAS)、线程安全地更新一个变量的方式。因为变量的类型有很多种,所以在Atomic包里一共提供了13个类,属于4种类型的原子更新方式,分别是原子更新基本类型、原子更新数组、原子更新引用和原子更新属性(字段)。Atomic包里的类基本都是使用Unsafe
实现的包装类。
使用原子操作类,简单,方法见名知义。
特别的,如果需要原子性地更新某个类里的某个字段时,就需要使用原子更新字段类,Atomic包提供了以下3个类进行原子字段更新:
-
AtomicIntegerFieldUpdater
:原子更新整型的字段的更新器。 AtomicLongFieldupdater
:原子更新长整型字段的更新器。AtomicStampedReference
:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更新数据和数据的版本号,可以解決使用CAS进行原子更新时可能出现的ABA问题。
要想原子地更新字段类需要两步:
- 因为原子更新字段类都是抽象类,每次使用的时候必须使用静态方法
newUpdater
创建一个更新器,并且需要设置想要更新的类和属性 - 更新类的宇段(属性)必须使用
public volatile
修饰符
对于原子更新数组类和原子更新对象引用类,实质上就是将数组和对象的外层使用Atomic封装,因此在进行替换的时候,其实是外层这个封装保证了原子性。同理,原子更新基本类型,如Integer,也是一个对象,同样也是在外层进行了一次封装。