介绍
AtomicBoolean 是使用CAS实现的对布尔值进行原子更新的类。
什么是CompareAndSwap(CAS)? 即比较并替换,实现并发算法时常用到的一种技术。CAS操作包含三个操作数——内存位置、预期原值及新值。执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作。CAS是一条CPU的原子指令(cmpxchg指令),不会造成所谓的数据不一致问题,AtomicBoolean 调用 Unsafe 类提供的CAS方法(如compareAndSwapXXX)实现了原子操作。要了解 Unsafe 的基本运用,请查看 《Unsafe的基本操作》
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicBoolean.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
查看源码可知:AtomicBoolean 类在实例化时会首先获取 value 的偏移量(valueOffset),后续方法中 Unsafe 类都会根据 valueOffset 进行数据操作。
并且 value 需要声明为 volatile ,以保证并发时线程间的可见性。
基本操作
以下以JDK 8中的源码进行示例。
1、创建 AtomicBoolean 实例
AtomicBoolean 提供了两个构造方法可供使用,如下:
public AtomicBoolean(boolean initialValue) {
value = initialValue ? 1 : 0;
}
public AtomicBoolean() {
}
因此实例化 AtomicBoolean 时按需调用对应的构造函数即可。
2、获取值
可以使用 get() 方法:
AtomicBoolean atomicBoolean = new AtomicBoolean();
boolean value = atomicBoolean.get();
System.out.println(value); // 输出:false
3、使用 compareAndSet 进行原子更新
compareAndSet 源码如下:
public final boolean compareAndSet(boolean expect, boolean update) {
int e = expect ? 1 : 0;
int u = update ? 1 : 0;
return unsafe.compareAndSwapInt(this, valueOffset, e, u);
}
可知,该方法只会调用一次 CAS 方法进行原子更新,若失败,则返回false,否则,返回 true;但是不会进行失败重试。
原子更新还有一个 weakCompareAndSet 方法:
public boolean weakCompareAndSet(boolean expect, boolean update) {
int e = expect ? 1 : 0;
int u = update ? 1 : 0;
return unsafe.compareAndSwapInt(this, valueOffset, e, u);
}
由于其实现与 compareAndSet 一致,因此,该方法调用结果也与 compareAndSet 一致。
4、设置值
可以使用 set() 方法对值进行重新设置,能保证线程可见性,非常简单。
atomicBoolean.set(true);
也可以使用 lazySet() 进行值设置:
public final void lazySet(boolean newValue) {
int v = newValue ? 1 : 0;
unsafe.putOrderedInt(this, valueOffset, v);
}
底层实现为 unsafe.putOrderedInt ,不保证线程可见性。
5、核心操作 getAndSet
以原子方式设置为给定值并返回前一个值,底层实现如下:
public final boolean getAndSet(boolean newValue) {
boolean prev;
do {
prev = get();
} while (!compareAndSet(prev, newValue));
return prev;
}
可知,底层使用循环不断重试调用 compareAndSet ,直到 compareAndSet 返回 true(即设置成功)为止。
总结:在开发过程中,涉及到的核心方法是 getAndSet ,可保证并发情况下的数据一致性。