JUC学习记录(二)- AtomicInteger

AtomicInteger

内部调用了Unsafe的方法,保证原子性;

什么是原子性:指操作要么成功要么失败(失败则进行事务回滚)

从一个例子从开始学习AtomicInteger

  Executor executor = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
     
            executor.execute(() -> {
     
                for (int j = 0; j < 30; j++) {
     
                    a++;
                    System.out.println(atomicInteger.incrementAndGet());
                }

            });
        }

其中a的值最终小于300,而atomicInteger最终输出的值为300(顺序不定)。

a++其实可以分解成3个步骤:

  1. 从主存读取a
  2. a+1操作
  3. 将结果刷新到主存

而在多线程中,某一线程进行了a+1但是值还没刷新到主存中另一个线程又执行了读取的操作,从而导致最终的结果小于300,而atomicInteger则不存在此风险,下面分析它为什么不存在此风险。

我们看其主要的变量以及常用的API来分析它内部是如何实现的:

变量
    // 获取Unsafe实例
    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    // 通过Unsafe获取value的内存偏移量
    private static final long VALUE;

    static {
     
        try {
     
            VALUE = U.objectFieldOffset
                // 获取类中value变量的内存偏移量
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (ReflectiveOperationException e) {
     
            throw new Error(e);
        }
    }
    // 值
    private volatile int value;
常用API

从例子中的方法开始分析:

    /**
     * 实际调用Unsafe的方法 - CAS 
     * 
     * Atomically increments by one the current value.
     *
     * @return the updated value
     */
    public final int incrementAndGet() {
     
        // VALUE是当前值的偏移量
        return U.getAndAddInt(this, VALUE, 1) + 1;
    }


	// 最终调用Unsafe中的方法
	 public final int getAndAddInt(Object obj, long offset, int increment) {
     
        // value 当前值
         int value;
        do {
     
            // 根据偏移量获取当前值
            value = this.getIntVolatile(obj, offset);
            // while循环判断当前值与预期值是否一致,一致的话将新值替换给内存中的值
        } while(!this.compareAndSwapInt(obj, offset, value, value + increment));
		// 返回增加前的值
        return value;
    }

其他常用的API与之类似,核心都是调用Unsafe中的native方法

你可能感兴趣的:(java)