Steve's Blog

Talk is cheap, show me the code.

0%

原子类

java.util.concurrent.atomic包下,大致可分为4类。其中底层原理使用的都是由Unsafe类提供的CAS操作提供。

1. 基本类型原子类

a. AtomicBoolean

b. AtomicInteger

使用的主要参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// setup to use Unsafe.compareAndSwapInt for updates
// 使用Unsafe的compareAndSwapInt来进行原子自增操作
private static final Unsafe unsafe = Unsafe.getUnsafe();
// valueOffset表示在当前对象中,value变量的内存偏移量
private static final long valueOffset;

static {
try {
// 静态方法块对valueOffset进行初始化赋值,这个值在赋值后不再变化
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
// AtomicInteger的值
private volatile int value;

主要方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
// 使用Unsafe的getAndAddInt方法进行自增
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
// // 使用Unsafe的compareAndSwapInt进行替换
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

Unsafe的getAndAddInt方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* var1 : 要处理的对象
* var2 : 要处理的数据在var1中的偏置值
* var4 : 要新增到旧值的值
*/
public final int getAndAddInt(Object var1, long var2, int var4) {
// 修改之前的值
int var5;
do {
// 通过var1和var2拿到主内存中要修改值的最新值
var5 = this.getIntVolatile(var1, var2);
// 如果通过CAS替换成功,则退出循环,否则一直自旋
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

return var5;
}

c. AtomicLong

d. 原子类的问题

  • ABA问题
  • 只能修改单个元素
  • 并发量很高时,CAS只能成功一次,大量请求无法更新成功,只能不断自旋,十分消耗CPU自愿,性能很差

针对第一个ABA问题,JUC中提供了2个带版本号的原子类AtomicMarkableReferenceAtomicStampedReference,每次数据更新后都会更新数据的版本号,用来防止ABA问题发生;
针对只能修改单个元素的问题,JUC提供了AtomicReference类,它是一个泛型类,可以用来一次性更新多个数据;
针对并发量很高CAS性能变差的问题,JUC提供了LongAdder类用来提高性能。

2. 引用原子类型

b. AtomicReference

相比基本类型的原子类,AtomicReference可以针对多个变量进行原子更新。
变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 使用Unsafe的compareAndSwapObject来进行原子自增操作
private static final Unsafe unsafe = Unsafe.getUnsafe();
// valueOffset表示在当前对象中,value变量的内存偏移量
private static final long valueOffset;

static {
try {
// 静态方法块对valueOffset进行初始化赋值,这个值在赋值后不再变化
valueOffset = unsafe.objectFieldOffset
(AtomicReference.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}

private volatile V value;

主要方法
1
2
3
4
5
6
7
8
9
10
11
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(V expect, V update) {
return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}

1
2
3
4
5
/**
* 底层是通过调用JNI实现原子更新
*
*/
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);

c. AtomicMarkableReference & AtomicStampedReference

AtomicMarkableReference 和 AtomicStampedReference 的区别是前者判断是否被更新过使用时使用一个boolean变量mark,后者使用一个int类型的stamp字段判断

AtomicMarkableReference

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 静态内部类,
* 一个成员变量 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);
}
}
/**
* 后续数据都针对静态内部类型的pair字段处理
*
*/
private volatile Pair<V> pair;

主要方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* Atomically sets the value of both the reference and mark
* to the given update values if the
* current reference is {@code ==} to the expected reference
* and the current mark is equal to the expected mark.
*
* @param expectedReference the expected value of the reference
* @param newReference the new value for the reference
* @param expectedMark the expected value of the mark
* @param newMark the new value for the mark
* @return {@code true} if successful
*/
public boolean compareAndSet(V expectedReference,
V newReference,
boolean expectedMark,
boolean newMark) {
Pair<V> current = pair;
// 有3
return
expectedReference == current.reference &&
expectedMark == current.mark &&
((newReference == current.reference &&
newMark == current.mark) ||
casPair(current, Pair.of(newReference, newMark)));
}

AtomicStampedReference

1
2
3
4
5
6
7
8
9
10
11
12
13
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* Atomically sets the value of both the reference and stamp
* to the given update values if the
* current reference is {@code ==} to the expected reference
* and the current stamp is equal to the expected stamp.
*
* @param expectedReference the expected value of the reference
* @param newReference the new value for the reference
* @param expectedStamp the expected value of the stamp
* @param newStamp the new value for the stamp
* @return {@code true} if successful
*/
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)));
}

3. 数组原子类

a. AtomicIntegerArray

原子更新int数组中的元素。
常量,变量和static代码块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* unsafe持有对象
*/
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);
// 如果不是2的幂次方,抛异常
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
//
shift = 31 - Integer.numberOfLeadingZeros(scale);
}

有两个构造方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 创建一个长度为length的原子int数组类对象
*/
public AtomicIntegerArray(int length) {
array = new int[length];
}

/**
* 传入一个int数组,创建一个长度和其相等的原子int数组类对象
* 所有元素都会复制到新的原子int数组类对象内
*/
public AtomicIntegerArray(int[] array) {
// Visibility guaranteed by final field guarantees
this.array = array.clone();
}

常用方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* Returns the length of the array.
*
* @return the length of the array
*/
public final int length() {
return array.length;
}

/**
* Gets the current value at position {@code i}.
*
* @param i the index
* @return the current value
*/
public final int get(int i) {
return getRaw(checkedByteOffset(i));
}

private int getRaw(long offset) {
return unsafe.getIntVolatile(array, offset);
}

getRaw()方法
1
2
3
4
5
6
7
8
9
10
11
12
13

private long checkedByteOffset(int i) {
if (i < 0 || i >= array.length)
throw new IndexOutOfBoundsException("index " + i);

return byteOffset(i);
}
private static long byteOffset(int i) {
return ((long) i << shift) + base;
}
private int getRaw(long offset) {
return unsafe.getIntVolatile(array, offset);
}

b. AtomicLongArray

原子更新long数组中的元素。

(3)AtomicReferenceArray

参考文档

[1] 死磕 java原子系列之终结篇
[2] Java魔法类:Unsafe应用解析