一. 原子更新基本类型
使用原子的方式更新基本类型,Atomic包提供了以下3个类:
- AtomicBoolean
- AtomicInteger
- AtomicLong
这三个类的用法和原理类似,这里以AtomicInteger为例详细分析下其内部原理。
AtomicInteger
核心属性
private static final Unsafe unsafe = Unsafe.getUnsafe();
//valueOffset即为字段value的内存偏移地址
private static final long valueOffset;
private volatile int value;
static {
try {
//通过字段valueOffset的值可以定位到AtomicInteger对象中value的内存地址,从而可以根据CAS实现对value字段的原子操作。
valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
常用方法
1. getAndSet(int newValue)
getAndSet(int newValue)的作用是返回旧值,原子设置新值。
public final int getAndSet(int newValue) {
return unsafe.getAndSetInt(this, valueOffset, newValue);
}
从源码可知,调用Unsafe类的getAndSetInt()方法,其源码如下:
public final int getAndSetInt(Object o, long offset, int newValue) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, newValue));
return v;
}
getAndSetInt()原理也很简单,主要通过调用native方法compareAndSwapInt()来实现原子更新。
2. compareAndSet(int expect, int update)
compareAndSet(int expect, int update)方法为典型的CAS操作,直接调用Unsage类的native方法compareAndSwapInt()来进行原子更新。
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
3. getAndIncrement()
getAndIncrement()方法的作用是对旧值加1,并返回旧值。
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
其主要调用了Unsafe的getAndAddInt()方法,代码如下:
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, v + delta));
return v;
}
其他的几个方法的原理也是类似,这里不再赘述,代码还是比较简单易读的。
二. 原子更新数组
使用原子的方式更新数组里的某个元素,Atomic包提供了以下3个类:
- AtomicIntegerArray: 原子更新整型数组里的元素
- AtomicLongArray: 原子更新长整型数组里的元素。
- AtomicReferenceArray: 原子更新引用类型数组里的元素。
这三个类的用法和原理类似,这里以AtomicIntegerArray为例详细分析下其内部原理。
AtomicIntegerArray
核心属性
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final int base = unsafe.arrayBaseOffset(int[].class);
private static final int shift;
private final int[] array;
static {
int scale = unsafe.arrayIndexScale(int[].class);
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
shift = 31 - Integer.numberOfLeadingZeros(scale);
}
常用方法
1. addAndGet(int i, int delta)
-
addAndGet(int i, int delta)
方法以原子方式将输入值delta
与数组中索引i
的元素相加,并返回新值。 -
getAndAdd(int i, int delta)
方法以原子方式将输入值delta
与数组中索引i
的元素相加,并返回旧值。
public final int addAndGet(int i, int delta) {
return getAndAdd(i, delta) + delta;
}
public final int getAndAdd(int i, int delta) {
return unsafe.getAndAddInt(array, checkedByteOffset(i), delta);
}
Unsafe类的getAndAddInt(Object o, long offset, int delta)
方法通过CAS的方式原子更新对象o的属性值。
//通过CAS的方式将对象o的属性加delta,并返回旧值。
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, v + delta));
return v;
}
2. compareAndSet(int i, int expect, int update)
compareAndSet方法会判断当前值如果等于预期值,则以原子方式将数组位置i的元素设置成update值。
public final boolean compareAndSet(int i, int expect, int update) {
return compareAndSetRaw(checkedByteOffset(i), expect, update);
}
private boolean compareAndSetRaw(long offset, int expect, int update) {
return unsafe.compareAndSwapInt(array, offset, expect, update);
}
三. 原子更新引用类型
通过原子的方式更新引用类型,Atomic包提供了3个类:
- AtomicReference: 原子更新引用类型
- AtomicReferenceFieldUpdater: 原子更新引用类型里的字段
- AtomicMarkableReference: 原子更新带有标记位的引用类型,可以原子更新一个布尔类型的标记位和引用类型。
AtomicReference
核心属性
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
//通过字段valueOffset的值可以定位到对象中value的内存地址,从而可以根据CAS实现对value字段的原子操作。
valueOffset = unsafe.objectFieldOffset
(AtomicReference.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile V value;
常用方法
1. compareAndSet(V expect, V update)
public final boolean compareAndSet(V expect, V update) {
return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}
简单示例:
public static void main(String[] args) {
AtomicReference<User> userRef = new AtomicReference<>();
boolean res1 = userRef.compareAndSet(null, new User(1, "1"));
System.out.println(res1);
System.out.println(userRef.get());
//需要同一个对象才能更新成功
boolean res2 = userRef.compareAndSet(new User(1, "1"), new User(2, "2"));
System.out.println(res2);
System.out.println(userRef.get());
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class User {
private Integer id;
private String name;
}
输出为:
true
App.User(id=1, name=1)
false
App.User(id=1, name=1)
AtomicReferenceFieldUpdater
AtomicReferenceFieldUpdater的主要作用是可对指定类的指定volatile
字段进行原子更新。
示例如下:
public class Node {
private volatile Node left, right;
private static final AtomicReferenceFieldUpdater<Node, Node> leftUpdater =
AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "left");
private static AtomicReferenceFieldUpdater<Node, Node> rightUpdater =
AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "right");
public Node getLeft() { return left; }
public boolean compareAndSetLeft(Node expect, Node update) {
return leftUpdater.compareAndSet(this, expect, update);
}
}
AtomicMarkableReference
AtomicMarkableReference类封装了一个对象的引用reference和一个布尔值mark,可以原子性地对这两个值进行更新。
核心属性
private static class Pair<T> {
final T reference;
final boolean mark;
private Pair(T reference, boolean mark) {
this.reference = reference;
this.mark = mark;
}
static <T> Pair<T> of(T reference, boolean mark) {
return new Pair<T>(reference, mark);
}
}
private volatile Pair<V> pair;
常用方法
1. 构造函数
public AtomicMarkableReference(V initialRef, boolean initialMark) {
pair = Pair.of(initialRef, initialMark);
}
2. get(boolean[] markHolder)
//markHolder[0]返回标记,返回值返回引用对象
public V get(boolean[] markHolder) {
Pair<V> pair = this.pair;
markHolder[0] = pair.mark;
return pair.reference;
}
3. set(V newReference, boolean newMark)
//如果引用对象或者标记不同,则重新赋值
public void set(V newReference, boolean newMark) {
Pair<V> current = pair;
if (newReference != current.reference || newMark != current.mark)
this.pair = Pair.of(newReference, newMark);
}
4. compareAndSet(V expectedReference, V newReference, boolean expectedMark, boolean newMark)
//主要通过UnSafe类的CAS方法更新Pair对象
public boolean compareAndSet(V expectedReference,
V newReference,
boolean expectedMark,
boolean newMark) {
Pair<V> current = pair;
return
expectedReference == current.reference &&
expectedMark == current.mark &&
((newReference == current.reference &&
newMark == current.mark) ||
casPair(current, Pair.of(newReference, newMark)));
}
private boolean casPair(Pair<V> cmp, Pair<V> val) {
return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}
四. 原子更新字段
原子更新字段类可以原子地更新某个类里的某个字段,Atomic包提供了以下3个类进行原子字段更新。
- AtomicIntegerFieldUpdater: 原子更新整型字段的更新器。
- AtomicLongFieldUpdater: 原子更新长整型字段的更新器。
- AtomicStampedReference: 原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子更新数据和数据的版本号,可以解决使用CAS进行原子更新时可能出现的ABA问题。
原子更新字段的流程有两步:
- 原子更新字段类都是抽象类,每次使用的时候必须使用静态方法newUpdater()创建一个更新器,并且需要指定想要更新的类和属性。
- 更新类的字段(属性)必须使用
public volatile
修饰符。
AtomicIntegerFieldUpdater
下面的示例代码需要注意的是,age的类型必须为public volatile int
,否则会报错,其他的方法使用和原理与AtomicInteger类似。
public static void main(String[] args) {
AtomicIntegerFieldUpdater<User> updater = AtomicIntegerFieldUpdater.newUpdater(User.class, "age");
User user = new User(1, 1);
//加1并返回更新后的age
System.out.println(updater.addAndGet(user, 1));
//输出age
System.out.println(updater.get(user));
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class User {
private Integer id;
public volatile int age;
}
AtomicStampedReference
核心属性
AtomicStampedReference类除了维护一个引用对象之外,还附带一个stamp属性,相当于版本号,可以避免CAS可能出现的ABA问题。
private static class Pair<T> {
final T reference;
final int stamp;
private Pair(T reference, int stamp) {
this.reference = reference;
this.stamp = stamp;
}
static <T> Pair<T> of(T reference, int stamp) {
return new Pair<T>(reference, stamp);
}
}
private volatile Pair<V> pair;
常用方法
1. 构造函数
public AtomicStampedReference(V initialRef, int initialStamp) {
pair = Pair.of(initialRef, initialStamp);
}
2. set(V newReference, int newStamp)
//设置时,reference或者stamp两者有一个不同就会更新pair
public void set(V newReference, int newStamp) {
Pair<V> current = pair;
if (newReference != current.reference || newStamp != current.stamp)
this.pair = Pair.of(newReference, newStamp);
}
3. get(int[] stampHolder)
//stampHolder[0]返回stamp,返回值返回reference
public V get(int[] stampHolder) {
Pair<V> pair = this.pair;
stampHolder[0] = pair.stamp;
return pair.reference;
}
4. compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)
//与AtomicMarkableReference很类型,只不过AtomicMarkableReference的mark为boolean类型
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair;
return
expectedReference == current.reference &&
expectedStamp == current.stamp &&
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}
private boolean casPair(Pair<V> cmp, Pair<V> val) {
return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}
五. 参考资料
- 《Java并发编程的艺术》