1.问题提出
先看下AtomicInteger的类属性和成员变量:
// setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(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. * * @return the previous value */ public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); } /** * Atomically decrements by one the current value. * * @return the previous value */ public final int getAndDecrement() { return unsafe.getAndAddInt(this, valueOffset, -1); }
先看下运行结果:
方法的返回值是老值,这个别用错了
这是我们看到了 unsafe的方法:
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; }
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. * * @return the current value */ public final int get() { return value; } /** * Sets to the given value. * * @param newValue the new value */ public final void set(int newValue) { value = newValue; }
最简单的get set 方法因为成员变量是volatile修饰,这样既可保证了内存的可见性。
c.
其余方法就不一一罗列,但是其基本原理皆为unsafe.compareAndSwapInt()的,总结老说就是利用了CAS来保证了原子性。