1.问题提出
先看下AtomicInteger的类属性和成员变量:
//setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe =Unsafe.getUnsafe();private static final longvalueOffset;static{try{
valueOffset=unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
}catch (Exception ex) { throw newError(ex); }
}private volatile int value;
可以看到:
1.AtomicInteger类使用Unsafe,直接操作内存 来保证 原子性。
2.还有个long类型的 valueOffset。并且在类加载时,初始化了值。
3.有一个用volatile修饰的成员变量 int 类型 value。
看到这,其中value明显就是AtomicInteger所包装的值,即我们用得到的值。但是valueOffset值代表的是什么呢? 还有这个unsafe是怎么操作的?
2.valueOffset
看其初始化的赋值方法,是unsafe的一个native方法:
public native long objectFieldOffset(Field var1);
objectFieldOffset方法,返回成员属性在内存中的地址相对于对象内存地址的偏移量 (来自百度)
那么对于每个对象来说,偏移量都是固定的,所以作为一个类变量。
那么对象的内存地址+偏移量 就可以知道成员变量value在内存中的具体地址了,此时我们就可以操作它了,此时java从“安全”变得“不安全”了。
3.关于unsafe
unsafe是一个用于直接操作内存的类,那么 我们通过AtomicInteger的方法来看下用法。
a、先看下自增自减 这种典型的非原子操作 AtomicInteger是如何做的。
/*** Atomically increments by one the current value.
*
*@returnthe previous value*/
public final intgetAndIncrement() {return unsafe.getAndAddInt(this, valueOffset, 1);
}/*** Atomically decrements by one the current value.
*
*@returnthe previous value*/
public final intgetAndDecrement() {return unsafe.getAndAddInt(this, valueOffset, -1);
}
先看下运行结果:
方法的返回值是老值,这个别用错了
这是我们看到了 unsafe的方法:
public final int getAndAddInt(Object var1, long var2, intvar4) {intvar5;do{
var5= this.getIntVolatile(var1, var2);
}while(!this.compareAndSwapInt(var1, var2, var5, var5 +var4));returnvar5;
}
Unsafe.getObjectVolatile():
获取所给对象的所给变量的值,使用volatile语义的load语义,会在实际获取这个值的时候从主存中加载,不会使用CPU缓存中的,总能确保获取到的是有效的值
compareAndSwapInt(var1, var2, var5, var5 +var4):
首先找出Object var1在内存中的位置p, 然后偏移var2个字节, 设p+var2处的这个int值为y,
如果y ==var5, 则执行赋值操作y = var5+var4, 返回true
如果y != var5, 则不执行赋值操作, 返回false
那个unsafe.getAndAddInt(Object var1,long var2,int var4)的代码我们就读懂了:
1.取到队形var1的偏移量var2下的成员变量的值,读取值后,作为期望值。
2. 在赋值操作的时候,先从内存中取到值和期望值比较,如果相等,则进行运算赋值操作,返回成功,结束。
3.否则,循环第一步。
其实就是 在unsafe中实现了CAS。
接着看AtomicInteger的其他方法
b.
/*** Gets the current value.
*
*@returnthe current value*/
public final intget() {returnvalue;
}/*** Sets to the given value.
*
*@paramnewValue the new value*/
public final void set(intnewValue) {
value=newValue;
}
最简单的get set 方法因为成员变量是volatile修饰,这样既可保证了内存的可见性。
c.
其余方法就不一一罗列,但是其基本原理皆为unsafe.compareAndSwapInt()的,总结老说就是利用了CAS来保证了原子性。