AtomicInteger
内部调用了Unsafe的方法,保证原子性;
什么是原子性:指操作要么成功要么失败(失败则进行事务回滚)
从一个例子从开始学习AtomicInteger
Executor executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
executor.execute(() -> {
for (int j = 0; j < 30; j++) {
a++;
System.out.println(atomicInteger.incrementAndGet());
}
});
}
其中a的值最终小于300,而atomicInteger最终输出的值为300(顺序不定)。
a++其实可以分解成3个步骤:
- 从主存读取a
- a+1操作
- 将结果刷新到主存
而在多线程中,某一线程进行了a+1但是值还没刷新到主存中另一个线程又执行了读取的操作,从而导致最终的结果小于300,而atomicInteger则不存在此风险,下面分析它为什么不存在此风险。
我们看其主要的变量以及常用的API来分析它内部是如何实现的:
变量
// 获取Unsafe实例
private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
// 通过Unsafe获取value的内存偏移量
private static final long VALUE;
static {
try {
VALUE = U.objectFieldOffset
// 获取类中value变量的内存偏移量
(AtomicInteger.class.getDeclaredField("value"));
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
// 值
private volatile int value;
常用API
从例子中的方法开始分析:
/**
* 实际调用Unsafe的方法 - CAS
*
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
// VALUE是当前值的偏移量
return U.getAndAddInt(this, VALUE, 1) + 1;
}
// 最终调用Unsafe中的方法
public final int getAndAddInt(Object obj, long offset, int increment) {
// value 当前值
int value;
do {
// 根据偏移量获取当前值
value = this.getIntVolatile(obj, offset);
// while循环判断当前值与预期值是否一致,一致的话将新值替换给内存中的值
} while(!this.compareAndSwapInt(obj, offset, value, value + increment));
// 返回增加前的值
return value;
}
其他常用的API与之类似,核心都是调用Unsafe中的native方法