Java从JDK5开始提供了java.util.concurrent.atomic包,包中的原子操作类提供一种用法简单、性能高效、线程安全地更新一个变量的方式。
因为变量的类型有很多种,所以在Atomic包一共提供了13个类,属于4种类型的原子更新方式,分别是:
Atomic包里的类基本都是使用Unsafe实现的包装类
值得一提的是,以AtomicInteger其内部用于存储目标整数值的变量是用volatile进行修饰的
public class AtomicInteger extends Number implements java.io.Serializable {
......
private volatile int value;
public AtomicInteger(int initialValue) {
value = initialValue;
}
......
}
AtomicBoolean:原子更新布尔类型AtomicInteger:原子更新整型AtomicLong:原子更新长整型三个类提供的方法几乎一模一样,所以以AtomicInteger为例
| 方法 | 描述 |
|---|---|
public final int addAndGet(int delta) | 以原子方式将输入的数值和实例中的值相加,并返回结果 |
public final boolean compareAndSet(int expect, int update) | 如果输入的数值等于预期值,则以原子方式将该值设置为输入的值 |
public final int getAndIncrement() | 以原子方式将当前值加1,注意,这里返回的是自增前的值 |
public final void lazySet(int newValue) | 最终会设置为newValue,使用lazySet设置值后,可能会导致其他线程在一小段时间内还是可以读到旧的值 |
public final int getAndSet(int newValue) | 用原子的方式设置位newValue,并返回旧值 |
Atomic包提供了3种基本类型的原子更新,以AtomicBoolean源码为例:
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);
}
可以发现它是先把Boolean转为整型,在使用compareAndSwapInt进行CAS,所以原子更新char,float,double变量也可以用类似的思路来实现。
通过原子的方式更新数组里的某个元素,Atomic包提供了以下4个类
AtomicIntegerArray:原子更新整型数组里的元素AtomicLongArray:原子更新长整型数组里的元素AtomicReferenceArray:原子更新引用类型数组里的元素public static void main(String[] args) {
static int[] value = new int[]{1,2};
static AtomicIntegerArray ai = new AtomicIntegerArray(value);
//getAndSet(下标,新元素)方法是用原子方式将新元素放置于指定下标位置,并返回旧值
ai.getAndSet(0, 3);
System.out.println(ai.get(0)); //输出 3
System.out.println(value[0]); //输出 1
}
这里值得注意的是,数组value通过构造方法传递进去,然后AtomicIntegerArray会将当前数组复制一份,然后当AtomicIntegerArray对内部的数组元素进行修改时,不会影响传入的数组。
public AtomicIntegerArray(int[] array) {
// Visibility guaranteed by final field guarantees
this.array = array.clone();
}
原子更新基本类型的AtomicInteger只能更新一个变量,如果是要原子更新多个变量,就需要使用这个原子更新引用类型提供的类
AtomicReference:原子更新引用类型AtomicReferenceFieldUpdater:原子更新引用类型里的字段AtomicMarkableReference:原子更新带有标志位的引用类型以AtomicReference为例
public class AtomicReferenceTest {
public static AtomicReference<user> atomicUserRef = new
AtomicReference<user>();
public static void main(String[] args) {
User user = new User("conan", 15);
atomicUserRef.set(user);
User updateUser = new User("Shinichi", 17);
//用CAS的方式更新引用
atomicUserRef.compareAndSet(user, updateUser);
System.out.println(atomicUserRef.get().getName());
System.out.println(atomicUserRef.get().getOld());
}
static class User {
private String name;
private int old;
public User(String name, int old) {
this.name = name;
this.old = old;
}
public String getName() {
return name;
}
public int getOld() {
return old;
}
}
}
如果需要原子地更新某个类里的某个字段时,就需要使用原子更新字段类,Atomic包提供了以下3个类进行原子字段更新
AtomicIntegerFieldUpdater:原子更新整型的字段的更新器AtomicLongFieldUpdater:原子更新长整型字段的更新器AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引 用关联起来,可用于原子的更新数据和数据的版本号,可以解决使用 CAS 进行 原子更新时可能出现的 ABA 问题以AtomicIntegerFieldUpdater为例:
public class AtomicIntegerFieldUpdaterTest {
// 创建原子更新器,并设置需要更新的对象类和对象的属性
//由于AtomicIntegerFieldUpdater是一个抽象类,所以使用的时候需要使用静态方法:newUpadter()创建一个更新器,并且设置想要更新的类和属性;并且更新类的字段(属性)必须使用 public volatile 修饰符
private static AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater.
newUpdater(User.class, "old");
public static void main(String[] args) { // 设置柯南的年龄是 10 岁
User conan = new User("conan", 10);
// 柯南长了一岁,但是仍然会输出旧的年龄 ===》输出:10
System.out.println(a.getAndIncrement(conan));
// 输出柯南现在的年龄 ===》输出:11
System.out.println(a.get(conan));
}
public static class User {
private String name;
public volatile int old;
public User(String name, int old) {
this.name = name;
this.old = old;
}
public String getName() {
return name;
}
public int getOld() {
return old;
}
}
}