AtomicBoolean可以用于原子的更新boolean变量的值,但不可用于替换java.lang.Boolean。
AtomicBoolean依赖CAS原始实现,在多线程高并发环境下,能保证只有一个线程对AtomicBoolean变量的修改有效。
演示示例:
package com.securitit.serialize.atomics;
import java.util.concurrent.atomic.AtomicBoolean;
public class AtomicBooleanTester {
// 初始化AtomicBoolean变量bool值为false.
private static AtomicBoolean bool = new AtomicBoolean(false);
public static void main(String[] args) {
// 通过CAS设置bool变量值为true.
// 即使在多线程环境下,仍然可以保持正常的程序语义.
if(bool.compareAndSet(false, true)) {
// 若更新成功,打印bool的值.
System.out.println(bool.get());
}
}
}
输出结果:
true
源码分析:
同样让我们分析一下AtomicBoolean类中的API。
实现基础:
private volatile int value;
AtomicBoolean类中有一个volatile int类型变量,AtomicBoolean的所有操作都是基于此int类型变量在0和1之间的变换实现的。具体volatile的特性以及内存实现方式,可以参照本博介绍volatile的文章。volatile能保证单一赋值操作的原子性。
构造方法:
public AtomicBoolean();
public AtomicBoolean(boolean initialValue);
AtomicBoolean提供了两种构造方法,无参构造方法内无实现,初始值取决于成员变量value的初始值,由于是int类型,初始值为0,即false。一个参数构造方法,传入参数作为初始值。
普通取值/赋值:
public final boolean get();
public final void set(boolean newValue);
AtomicBoolean提供了get/set用于进行普通赋值操作,这两个操作在多线程环境下,由于不具备CAS处理功能,所以可能导致意想不到的问题。
CAS取值/赋值:
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);
}
public boolean weakCompareAndSet(boolean expect, boolean update) {
int e = expect ? 1 : 0;
int u = update ? 1 : 0;
return unsafe.compareAndSwapInt(this, valueOffset, e, u);
}
public final boolean getAndSet(boolean newValue) {
boolean prev;
do {
prev = get();
} while (!compareAndSet(prev, newValue));
return prev;
}
以上三个方法都是依赖CAS来实现的,差别在于:
· getAndSet:只允许传入新值,通过上面源码可以看出,此方法在get和set之间是不允许其它线程对其进行操作的,若set失败,会重新尝试。
· compareAndSet:指定期望值和新值,进行CAS比较替换操作。
· weakCompareAndSet:weakCompareAndSet的内部实现和compareAndSet一样,唯一区别在于方法本身compareAndSet由final修饰,而weakCompareAndSet没有。
官方文档注释:以原子的方式更新这个更新器所管理的对象(obj)的成员变量,并且将这个成员变量更新为给定的更新后的值(update)如果当前值等于期望值(expect)时,当存在其他使用compareAndSet或者set的情况下,这个方法可以确保是原子的,但如果你用其他的方式去改变这个成员变量时(如,使用直接赋值的方式 field=newField),那么它是不会遵循这个原子性的。该方法可能可能虚假的失败并且不会提供一个排序的保证,所以它在极少的情况下用于代替compareAndSet方法。
个人来看,weakCompareAndSet和compareAndSet至少在JDK1.8版本及之前,没有明显差别,两个方法的唯一差别只是weakCompareAndSet没有final修饰,所以不同厂商的虚拟机可以对其进行改造,根据自身指令级确定CAS实现,也就是不确定性的由来。
惰性赋值
public final void lazySet(boolean newValue);
AtomicBoolean操作都是依赖volatile int变量来实现,volatile关键字保证了int类型变量在多个线程中的可见性,lazySet的作用就是抵消这种保证,让volatile int变量可以如普通变量一样操作,在多线程环境中,恢复可见性问题。
注:文中源码均来自于JDK1.8版本,不同版本间可能存在差异。
如果有哪里有不明白或不清楚的内容,欢迎留言哦!