Java多线程中的原子类主要有:
- 基本数据类型操作:AtomicInteger、AtomicLong、AtomicBoolean
- 数组操作:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
- 对象的成员变量操作:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater、AtomicReference(对象的原子操作)
Atomic包里的类基本都是使用Unsafe(无锁)实现的包装类,核心操作是CAS原子操作;
Unsafe:
Java不能直接访问操作系统底层,而是通过本地方法来访问。Unsafe类提供了硬件级别的原子操作,主要提供了以下功能:
- 通过Unsafe类可以分配内存,可以释放内存;
- 可以定位对象某字段的内存位置,也可以修改对象的字段值,即使它是私有的;
- 通过compareAndSwapXXX方法实现CAS操作
关于CAS
- compare and swap,比较和替换技术,将预期值与当前变量的值比较(compare),如果相等则使用新值替换(swap)当前变量,否则不作操作;
- 现代CPU已广泛支持CAS指令,如果不支持,那么JVM将使用自旋锁,与互斥锁一样,两者都需先获取锁才能访问共享资源,但互斥锁会导致线程进入睡眠,而自旋锁会一直循环等待直到获取锁;
- 另外,有一点需要注意的是CAS操作中的ABA问题,即将预期值与当前变量的值比较的时候,即使相等也不能保证变量没有被修改过,因为变量可能由A变成B再变回A,解决该问题,可以给变量增加一个版本号,每次修改变量时版本号自增,比较的时候,同时比较变量的值和版本号即可;
CAS线程安全
- 可它为什么是线程安全的呢?这就是Atomic包下这些类的奥秘:语言层面不做处理,我们将其交给硬件—CPU和内存,利用CPU的多处理能力,实现硬件层面的阻塞,再加上volatile变量的特性即可实现基于原子操作的线程安全。所以说,CAS并不是无阻塞,只是阻塞并非在语言、线程方面,而是在硬件层面,所以无疑这样的操作会更快更高效!
- 虽然基于CAS的线程安全机制很好很高效,但要说的是,并非所有线程安全都可以用这样的方法来实现,这只适合一些粒度比较小,型如计数器这样的需求用起来才有效,否则也不会有锁的存在了。
从这些原子类的源码中可以知道,这些类除了继承Object类外,没有其它统一的实现接口或继承的父类,也就没有统一的特性。这里就以AtomicInteger、AtomicIntegerArray、AtomicReference、AtomicIntegerFieldUpdater为例进行简单说明
AtomicInteger
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value; //volatile关键字修饰的变量,内存可见
构造方法:
public AtomicInteger(int initialValue) {
value = initialValue;
}
public AtomicInteger() { }
常用方法:
- int get() 获取当前值。
- void set(int newValue) 设置为给定值。
- void lazySet(int newValue) 最后设置为给定值,不保证其他线程可见。
- int getAndSet(int newValue) 以原子方式设置为给定值,并返回旧值。
- int getAndDecrement() 以原子方式将当前值减 1,返回旧值。
- int getAndIncrement() 以原子方式将当前值加 1,返回旧值。
- int incrementAndGet() 以原子方式将当前值加 1,返回新值。
- int addAndGet(int delta) 以原子方式将给定值与当前值相加,返回新值。
- int decrementAndGet() 以原子方式将当前值减 1,返回新值。
- boolean compareAndSet(int expect, int update) 如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。
- double doubleValue() 以 double 形式返回。
- float floatValue() 以 float 形式返回。
- int intValue() 以 int 形式返回。
- long longValue() 以 long 形式返回。
- String toString() 返回当前值的字符串表示形式。
- boolean weakCompareAndSet(int expect, int update) 如果当前值 == 预期值,则以原子方式将该设置为给定的更新值。
看一下部分计算方法的源码:
public final int decrementAndGet() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return next;
}
}
public final int getAndAdd(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return current;
}
}
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}
可以看到,几个原子计算的方法中都调用了compareAndSet方法:
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
AtomicIntegerArray:
构造函数:
private final int[] array;//定义array变量
public AtomicIntegerArray(int length) {
array = new int[length];
}
public AtomicIntegerArray(int[] array) {
this.array = array.clone();
}
常用方法:
- int length() 返回该数组的长度。
- int get(int i) 获取位置 i 的当前值。
- void set(int i, int newValue) 将位置 i 的元素设置为给定值。
- void lazySet(int i, int newValue) 最后将位置 i 的元素设置为给定值。
- int getAndAdd(int i, int delta) 以原子方式将给定值与索引 i 的元素相加。
- int getAndDecrement(int i) 以原子方式将索引 i 的元素减 1。
- int getAndIncrement(int i) 以原子方式将索引 i 的元素加 1。
- int getAndSet(int i, int newValue) 将位置 i 的元素以原子方式设置为给定值,并返回旧值。
- int incrementAndGet(int i) 以原子方式将索引 i 的元素加 1。
- int addAndGet(int i, int delta) 以原子方式将给定值与索引 i 的元素相加。
- int decrementAndGet(int i) 以原子方式将索引 i 的元素减 1。
- boolean compareAndSet(int i, int expect, int update) 如果当前值 == 预期值,则以原子方式将位置 i 的元素设置为给定的更新值。
- String toString() 返回数组当前值的字符串表示形式。
- boolean weakCompareAndSet(int i, int expect, int update) 如果当前值 == 预期值,则以原子方式将位置 i 的元素设置为给定的更新值。
对比AtomicInteger和AtomicIntegerArray容易发现,两个类中的操作方法基本相同。
AtomicReference<T>:
构造方法:
public AtomicReference(T initialValue) {
value = initialValue;
}
public AtomicReference() { }
常用方法:
- T get() 获取当前值。
- T getAndSet(T newValue) 以原子方式设置为给定值,并返回旧值。
- void lazySet(T newValue) 最终设置为给定值。
- void set(T newValue) 设置为给定值。
- String toString() 返回当前值的字符串表示形式。
- boolean compareAndSet(V expect, V update) 如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。
- boolean weakCompareAndSet(T expect, T update) 如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。
abstract class AtomicIntegerFieldUpdater<T>:
AtomicIntegerFieldUpdater是一个抽象类,内部定义了一个自己的实现类,AtomicIntegerFieldUpdaterImpl<T> extends AtomicIntegerFieldUpdater<T>。内部定义了一个newUpdater方法,创建AtomicIntegerFieldUpdater 实例。
public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName) {
return new AtomicIntegerFieldUpdaterImpl<U>(tclass, fieldName, Reflection.getCallerClass());
}
常用方法:
- int addAndGet(T obj, int delta) 以原子方式将给定值添加到此更新器管理的给定对象的字段当前值。
- int decrementAndGet(T obj) 以原子方式将此更新器管理的给定对象的字段的当前值减 1。
- int getAndAdd(T obj, int delta) 以原子方式将给定值添加到此更新器管理的给定对象的当前值。
- int getAndDecrement(T obj) 以原子方式将此更新器管理的给定对象的当前值减 1。
- int getAndIncrement(T obj) 以原子方式将此更新器管理的给定对象的当前值加 1。
- int getAndSet(T obj, int newValue) 以原子方式将此更新器管理的给定对象的字段设置为给定值,并返回旧值。
- int incrementAndGet(T obj) 以原子方式将此更新器管理的给定对象的字段的当前值加 1。
- abstract void lazySet(T obj, int newValue) 最后将此更新器管理的给定对象的字段设置为给定更新值。
- abstract int get(T obj) 获取此更新器管理的在给定对象的字段中保持的当前值。
- abstract void set(T obj, int newValue) 将此更新器管理的给定对象的字段设置为给定更新值。
- abstract boolean weakCompareAndSet(T obj, int expect, int update) 如果当前值 == 预期值,则以原子方式将此更新器所管理的给定对象的字段值设置为给定的更新值。
- abstract boolean compareAndSet(T obj, int expect, int update) 如果当前值 == 预期值,则以原子方式将此更新器所管理的给定对象的字段值设置为给定的更新值。
其中的抽象方法,都是在它内部定义的实现类AtomicIntegerFieldUpdaterImpl中实现。
推荐参考文档:https://blog.csdn.net/fly910905/article/details/80737784