概述
在Atomic包里一共有13个类,四种原子更新方式,分别是原子更新基本类型,原子更新数组,原子更新引用和原子更新字段。Atomic包里的类基本都是使用Unsafe实现的包装类。
万变不离其宗,JDK提供的原子更新类和方法虽多,但最终依赖的都是CAS操作。
原子更新基本类型类
用于通过原子的方式更新基本类型,Atomic包提供了以下三个类:
1. AtomicBoolean:原子更新布尔类型
2. AtomicInteger:原子更新整型
3. AtomicLong:原子更新长整型
这三个类中的数据都是以volatile声明,所以get()和set()方法就可以达到volatile的内存语义。
compareAndSet()与weakCompareAndSet()是有条件的修改程序的方法,这两个方法都要取用两个参数:在方法启动时预期数据所具有的的值,以及要把数据所设定成的值。它们都只会在变量具有预期值的时候才会设定成新值,如果当前值不等于预期值,该变量就不会被重新赋值并且返回false。这两个方法之间有什么区别吗?第二个方法少了一项保证:如果方法返回的值false,该变量不会被变动,但是这并不表示现有值不是预期值,也就是说,这个方法不管初始值是否是预期值都可能会无法更新改值。
incrementAndGet(),decrementAndGet(),getAndIncrement()和getAndDecrement()提供了前置递增,前置递减,后递增和后递减,之所以有这些方法,是因为这些操作都不是atomic的。
addAndGet()和getAndAdd()提供了”前置”和”后置”的运算符给指定值的加法运算,它们能够让程序对变量增或者减一个指定值,包括了负值,所以我们就不需要一个相对的减法运算。
为一个AtomicLong对象设置一个值,jvm会确保其他线程读取到最新值,原子类和voliatile变量也是一样的,这是由依赖于硬件的系统指令(如x86的xchg)实现的。lazySet()却是无法保证这一点的方法,所以其他线程在之后的一小段时间里还是可以读到旧的值。
AtomicBoolean是先把Boolean转换成整型,再使用compareAndSwapInt进行CAS,所以原子更新char、float、double也可以用类似的思路来实现。
原子更新数组类
通过原子的方式更新数组里的某个元素,Atomic包提供了以下3个类:
1. AtomicIntegerArray:原子更新整型数组里的元素。
2. AtomicLongArray:原子更新长整型数组里的元素。
3. AtomicReferenceArray:原子更新引用类型数组里的元素。
原子更新引用类型
原子更新基本类型的AtomicInteger,只能更新一个变量,如果要原子的更新多个变量,就需要使用这个原子更新引用类型提供的类。Atomic包提供了以下三个类:
- AtomicReference:原子更新引用类型。
- AtomicReferenceFieldUpdater:原子更新引用类型里的字段。
- AtomicMarkableReference:原子更新带有标记位的引用类型。可以原子的更新一个布尔类型的标记位和引用类型。构造方法是AtomicMarkableReference(V initialRef, boolean initialMark)
public class AtomicReferenceTest {
public static AtomicReference<user> atomicUserRef = new AtomicReference</user><user>();
public static void main(String[] args) {
User user = new User("conan", 15);
atomicUserRef.set(user);
User updateUser = new User("Shinichi", 17);
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包提供了以下三个类:
1. AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
2. AtomicLongFieldUpdater:原子更新长整型字段的更新器。
3. AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更数据和数据的版本号,可以解决使用CAS进行原子更新时,可能出现的ABA问题。