为什么AtomicXXX是线程安全的累计计数
2017-07-12 本文已影响15人
battle_
effective java 第66条内容说到,累计计数i++同步问题
一种是用sync修饰符进行同步操作
另一种是使用AtomicXXX变量,如AtomicLong 的 getAndIncrement方法
好奇AtomicLong的实现,原以为也是进行同步锁操作实现的原子性。
发现源码并不是这样做的,而是使用了native方法unsafe,改方法只能在授信的代码中实例化如jdk内。
代码如下
public final long getAndIncrement() {
while (true) {
long current = get();
long next = current + 1;
if (compareAndSet(current, next))
return current;
}
}
compareAndSet
public final boolean compareAndSet(long expect, long update) {
return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
可以发现当进行累加操作时,第一时间没有进行对变量的更改操作
而是将累加结果保存,通过compareAndSet方法对变量更改,
使用unsafe CAS方法判断当前值与要累加的值expect对比,如果两个变量是同一个,则进行更改操作update->当前值,否则返回false CAS存在一个ABA问题,即当判断值A是否被修改前,该值被其他线程修改成了B,然后又被修改回了A,那么CAS仍然认为该值是没有被修改过的,进行赋值操作
而getAndIncrement原理就是,不断的loop循环判断,当前值有没有在累加操作前被其他线程修改了,如果没改就赋值,改了就重新累加,再判断赋值。从而形成了类似同步的机制。保证变量的原子性。