第7章 Java中的13个原子操作类
当一个线程更新一个变量时,程序如果没有正确的同步,那么这个变量对于其他线程来说是不可见的。我们通常使用synchronized或者volatile来保证线程安全的更新共享变量。在JDK1.5中,提供了java.util.concurrent.atomic包,这个包中的原子操作类提供了一种用法简单,性能高效,线程安全地更新一个变量的方式。
Atomic包里一共提供了13个类,有4种类型的原子更新方式:原子更新基本类型、原子更新数组、原子更新引用和原子更新属性。其实现基本都是使用Unsafe实现的包装类。
7.1 原子更新基本类型类
- AtomicBoolean:原子更新布尔类型
- AtomicInteger:原子更新整型
- AtomicLong:原子更新长整型
以上3个类提供的方法基本一致,我们以AtomicInteger为例进行分析。
AtomicInteger常用的方法有:
- int addAndGet(int delta):以原子方式将输入的数值与实例中的值相加,并返回结果。
- boolean compareAndSet(int expect,int upate):如果输入的数值等于预期值,则以原子方式将该值设置为输入的值。
- int getAndIncrement():以原子方式将当前值加1,返回自增前的值。
- void lazySet(int newValue):最终会设置成new Value,但可能导致其他线程在之后的一小段时间内还是可以读到旧的值。
- int getAndSet(int newValue):以原子方式设置为newValue,并返回旧值。
其实现依靠我们熟悉的CAS算法:
在Java的基本类型中除了Atomic包中提供原子更新的基本类型外,还由char、float和double。那么这些在Atomic包中没有提供原子更新的基本类型怎么保证其原子更新呢?
从AtomicBoolean源码中我们可以得到答案:首先将Boolean转换为整型,然后使用comareAndSwapInt进行CAS,所以原子更新char、float、double同样可以以此实现。
7.2 原子更新数组
- AtomicIntegerArray:原子更新整型数组里的元素。
- AtomicLongArray:原子更新长整型数组里的元素。
- AtomicReferenceArray:原子更新引用类型数组里的元素。
【备注:】看书上说原子更新数组有4个类,除了上述3个外,还有AtomicBooleanArray类,但我在jdk5/6/7/8中都没有找到这个类的存在,只找到共12个原子操作类,而不是标题中的13个。不知道是否是书中的错误?请知情的童鞋不吝赐教。
AtomicIntegerArray类主要提供原子的方式更新数组里的整型,其常用方法如下:
- int addAndGet(int i,int delta):以原子方式将输入值与数组中索引i的元素相加。
- boolean compareAndSet(int i,int expect,int update):如果当前值等于预期值,就把索引i的元素设置成update值。
【备注】:在AtomicIntegerArray构造方法中,AtomicIntegerArray会将数组复制一份,所以当其对内数组元素进行修改时,不会影响原传入数组。
7.3 原子更新引用类型
原子更新基本类型的AtomicInteger,只能更新一个变量,如果需要原子更新多个变量,就需要使用这个原子更新引用类型提供的类。Atomic包提供以下3个类:
- AtomicReference:原子更新引用变量。
- AtomicReferenceFieldUpdater:原子更新引用类型里的字段。
- AtomicMarkableReference:原子更新带有标记位的引用类型 。可以原子更新一个布尔类型的标记位和引用类型。构造方法时AtomicMarkableReference(V initialRef,boolean initialMark)。
7.4 原子更新字段类
如果需要原子的更新某个类里的某个字段,就需要使用原子更新字段类,Atomic包提供了以下3个类进行原子字段更新:
- AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
- AtomicLongFiledUpdater:原子更新长整型字段的更新器。
- AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,用于原子的更新数据和数据的版本号,避免CAS的ABA问题、
想要原子的更新字段类需要调用静态方法newUpdater()创建一个更新器,并设置想要更新的类和属性。且更新类的字段必须使用public volatile修饰符。