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来保证了原子性。

 

posted on 2020-04-02 18:36  rookie111  阅读(482)  评论(0编辑  收藏  举报