• java并发编程--AtomicInteger


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

  • 相关阅读:
    my functions.h
    MFC中GDI之CBitmap(位图)
    MFC中GDI之CFont(字体)
    MFC中GDI之CBrush(画刷)
    MFC中SDI设置状态栏显示时间
    MFC中自定义消息
    MFC常用控件类
    MFC中改变窗口大小MoveWindow...
    MFC中建立关联变量的几种方式
    软件默认端口整理
  • 原文地址:https://www.cnblogs.com/rookie111/p/12622221.html
Copyright © 2020-2023  润新知