AtomicInteger 探究

115 阅读2分钟

AtomicInteger

  1. AtomicInteger 的作用是什么?
  2. AtomicInteger 在多线程下如何保持一致性?
  3. IntUnaryOperator / IntBinaryOperator 接口的作用是什么?

AtiomicInteger 的作用是什么?

Atomic是一个原子类,可以在多线程的条件下安全的实现整数操作问题

@Test
public void test1(){
    AtomicInteger atomicCounter = new AtomicInteger(0);

    // 使用线程池执行10次增加操作
    for (int i = 0; i < 10; i++) {
        new Thread(() -> {
            for (int j = 0; j < 100; j++) {
                atomicCounter.incrementAndGet();
            }
        }).start();
    }

    // 等待所有线程执行完毕
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    // 输出最终结果
    System.out.println("Counter: " + atomicCounter.get());
}

AtomicInteger 在多线程下如何保持一致性?

通过查看 AtomicInteger 的源码可以发现,它实现多线程安全是通过 volatile 关键字加上 CAS 自旋实现的

private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
​
static {
    try {
        valueOffset = unsafe.objectFieldOffset
          (AtomicInteger.class.getDeclaredField("value")); // 获取字段偏移量,在进行CAS操作时,需要通过偏移量来进行校验
    } catch (Exception ex) { throw new Error(ex); }
}
​
private volatile int value;  // 实际存储的值,通过 volatile 进行修改,保证了多线程情况下的可见性和有序性

下面这个方法表示了,如何实现多线程一致性。主要是通过 CAS + 不断的循环。其他的方法与其也非常类似,只是参数、返回值不同,整个处理逻辑都是一样的

public final int getAndUpdate(IntUnaryOperator updateFunction) {
    int prev, next;
    do {
        prev = get();
        next = updateFunction.applyAsInt(prev); // IntUnaryOperator是一个函数式接口,通过自己实现其方法为变量赋新值
    } while (!compareAndSet(prev, next));  // 其中 compareAndSet 是调用了本地方法的 compareAndSet(偏移量,旧值,新值),一直进行赋值,直至成功
    return prev;
}

需要注意的一点, 带 lazy 的方法则不保证可见性。如下面方法则不保证可见性

public final void lazySet(int newValue) {
    unsafe.putOrderedInt(this, valueOffset, newValue);
}

IntUnaryOperator / IntBinaryOperator 接口的作用是什么?

IntUnaryOperator 是一个一元函数,即只接受一个参数,返回一个结果。IntBinaryOperator 是一个二元函数,即接受两个参数返回一个结果

@Test
public void testGetAndUpdate() {
    AtomicInteger atomic = new AtomicInteger(); // 不带参数,默认初始化为0
    int oldValue = atomic.getAndUpdate(newValue -> newValue + 10); // 一元函数
    int newValue = atomic.get();
    System.out.println("oleValue is " + oldValue);
    System.out.println("newValue is " + newValue);
}
​
@Test
public void testGetAndAccumulate() {
    AtomicInteger atomic = new AtomicInteger(2); // 初始化为2
    int oldValue = atomic.getAndAccumulate(20, (preValue,b)->preValue*b); // 第二个参数是一个二元函数,二元函数中的第一个参数通过 atomic.get()得到,不需要传递,第二个参数需要进行传递。
    int newValue = atomic.get();
    System.out.println("oleValue is " + oldValue);
    System.out.println("newValue is " + newValue);
}