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;
}
过程是:
- 根据对象以及偏移量,让var5获取对象的值
- 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;