CAS与AtomicInteger自增操作

CAS:compareAndSet,对比然后赋值;

AtomicInteger中的:

  /**
    *this:操作对象
    *valueOffset:对象值偏移地址
    *expect:预期值
    *update:更新的值
    */
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

过程就是通过对象this以及值内存偏移量获取到对象目前真正的值,然后与expect比较,如果相等,就更新值为update,并返回true;如果内存中的值与预期值expect不相等,则不做修改,返回false;
很有乐观锁的感觉;

然后看一下AtomicInteger的原子性自增;

    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

点进getAndAddInt

/**
*var1:操作对象
*var2:对象值偏移地址
*var4:增加的值
*var5:获取对象的目前的值
**/
    public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

过程是:

  1. 根据对象以及偏移量,让var5获取对象的值
  2. compareAndSwapInt就是上面的CAS原子性操作,比较对象目前的值是否等于var5,再决定是否修改;如果compareAndSwapInt返回true,则自增成功,输入新值,并跳出循环;而如果为false,则自增失败,重新跳回第一步,重新执行;

之所以还要获取最新的值与var5比较,就是为了防止多线程下,有其它线程先对数据进行修改,导致出现错误的结果;这个也是在多线程下,AtomicInteger自增能正确的原因;

而普通的n++操作是不具有原子性操作的,因为这个并不是一条指令完成的;
流程是:
1.先取出n的值;
2.然后让n+1;
3.之后在将n的值写回去;
则在多线程的情况下,就不能保证这三条指令连接一起完成;

假设情况:n=0
线程A先取出n=0;
然后转为线程B,B也取出n=0,B将0+1=1然后存进去;
又转回线程A,此时A拿到的n仍然等于0,所以A将0+1=1存了进去;
则AB都对n做自增操作,但n最后等于1;

上一篇:Java引用和C++引用的区别


下一篇:自旋锁