一:为什么需原子操作类
当程序更新一个变量时,如果多个线程同时更新,那么我们可能得不到期望值。通常我们使用synchronized解决这个问题。
从JDK1.5开始,在java.util.concurrent.atomic 包的原子操作类提供了一种用法简单、高效、线程安全地更新一个变量的方式。
类 AtomicBoolean、AtomicInteger、AtomicLong 和 AtomicReference 的实例各自提供对相应类型单个变量的访问和更新。每个类也为该类型提供适当的实用工具方法。
二:原子更新基本数据类型
可以用原子方式更新的 boolean 值。 |
|
可以用原子方式更新的 int 值。 |
|
扫描二维码关注公众号,回复:
122880 查看本文章
|
可以用原子方式更新的 long 值。 |
下面以AtomicInteger为例:
int |
addAndGet(int delta) |
boolean |
compareAndSet(int expect, int update) |
int |
decrementAndGet() |
int |
get() |
int |
getAndAdd(int delta) |
int |
getAndDecrement() |
int |
getAndIncrement() |
int |
getAndSet(int newValue) |
int |
incrementAndGet() |
int |
intValue() |
对于这些方法:i. addAndGet(1); -> ++i
i. getAndAdd(1); -> i++
那么它们是如何实现原子操作的?
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
因为compareAndSwapInt的源码无法追到,我们采用分析的方式来解释CAS算法的逻辑。
多线程并发的情况下,如果this值与expect值相等,则将update值赋值给this,更新成功返回true。若this值与expect值不相等,则说另一个线程已经访问并修改了值,则返回false,说明更新失败。
代码没有加锁,那么另一个线程是如何得知值已经被修改了?
private volatile int value;
volatile修饰的变量,多线程间可见。一个线程修改了value值,其它正在访问value的线程也得到通知(value值已经被更改,值可见)。
三:原子跟新数组
可以用原子方式更新其元素的 int 数组。 |
|
可以用原子方式更新其元素的 long 数组。 |
|
可以用原子方式更新其元素的对象引用数组。 |
构造函数
AtomicIntegerArray(int length) |
AtomicIntegerArray(int[] array) |
public AtomicIntegerArray(int[] array) {
// Visibility guaranteed by final field guarantees
this.array = array.clone();
}
源代码分析:数组通过构造方法传递进去,然后AtomicIntegerArray会将传入的数组复制一份。所以AtomicIntegerArray对内部的元素进行操作时,不会影响传入的数组。
四:更新引用类型的数据
AtomicMarkableReference 维护带有标记位的对象引用,可以原子方式对其进行更新。 |
|
可以用原子方式更新的对象引用。 |
|
AtomicStampedReference 维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。 |
AtomicReference也实现了compareAndSet方法进行原子操作。
boolean |
compareAndSet(V expect, V update) |
五:原子更新字段类
基于反射的实用工具,可以对指定类的指定 volatile int 字段进行原子更新。 |
|
基于反射的实用工具,可以对指定类的指定 volatile long 字段进行原子更新 |
|
基于反射的实用工具,可以对指定类的指定 volatile 字段进行原子更新。 |
①原子更新字段类都是抽象类,每次使用都必须使用静态方法newUpdater()创建一个更新器,并且设置想要更新的类和属性。
②更新类的字段必须使用public volatile修饰符。