# AtomicInteger

AtomicInteger是java.util.concurrent.atomic包下提供的原子计数类,能够实现并发环境下的线程安全的int数据的读写、原子修改等操作,解决经典的i++的线程不安全问题。

使用场景

  1. 计数,保证原子性可见性防止重排序等,例如AQS中的state字段,和AtomicInteger是比较类似的,不过AQS为了提升性能直接通过Unsafe操作volatile变量
  2. 自旋锁,通过cas成功表示加锁成功,在LongAdder和ConcurrentHashMap中我们看到了使用cellsBusy作为自旋锁使用。

# 实现原理

AtomicInteger中定义了一个volatile的int类型的value字段,然后使用jdk.internal.misc.Unsafe类实现各种语义的value字段的读、写、cas等功能。 java9中提供了一个新的代替Unsafe类中的cas功能的VarHandle类,不过因为一些启动时循环依赖问题还没有解决,目前还没使用VarHandle。

public class AtomicInteger extends Number implements java.io.Serializable {

    private static final Unsafe U = Unsafe.getUnsafe();
    private static final long VALUE
            = U.objectFieldOffset(AtomicInteger.class, "value");

    private volatile int value;
}
1
2
3
4
5
6
7
8

incrementAndGet实现,incrementAndGet是原子加1后获取加之后的值,直接调用的Unsafe的getAndAddInt方法实现

public final int incrementAndGet() {
    return U.getAndAddInt(this, VALUE, 1) + 1;
}
1
2
3

getAndAddInt会通过cas给指定对象的指定offset的字段加上一个delta值,实现是循环中,先获取当前值,然后尝试cas,cas成功返回,失败重试。 weakCompareAndSetInt最终会调用到Unsafe的native方法compareAndSetInt,native具体的实现会根据不同的平台进行不同的实现,比如x86 CPU中通过lock前缀加cmpxchg来实现。

public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    do {
        v = getIntVolatile(o, offset);
    } while (!weakCompareAndSetInt(o, offset, v, v + delta));
    return v;
}
public final boolean weakCompareAndSetInt(Object o, long offset,
        int expected, int x) {
    return compareAndSetInt(o, offset, expected, x); 
}
public final native boolean compareAndSetInt(Object o, long offset,
        int expected, int x);
1
2
3
4
5
6
7
8
9
10
11
12
13

# 小应用,实现多线程顺序打印数字

最后我们以一个常见的面试题结束AtomicInteger的学习。 题目是让10个线程交替顺序打印出0-100。

这个题目有很多种解法,今天我们使用AtomicInteger实现其中一种,使用AtomicInteger保证线程间的可见性。 解决思路为,各个线程共享一个计数遍历,每个线程保存一个属于自己的计数,当共享的变量值等于自己的计数值时打印并增加计数。 这个题目其实没有使用太多AtomicInteger的原子操作能力,只是利用了它的可见性保证。

代码示例如下:

AtomicInteger atomicInteger = new AtomicInteger();
Thread[] threads = new Thread[10];
int threadCount = threads.length;
int totalCount = 100;
for (int i = 0; i < 10; i++) {
    int finalI = i;
    threads[i] = new Thread(() -> {
        int start = finalI;
        while (atomicInteger.get() <= totalCount) {
            if (atomicInteger.get() == start) {
                System.out.println("Thread: " + finalI + " " + start);
                atomicInteger.compareAndSet(start, start + 1);
                start = start + threadCount;
            }
        }
    });
    threads[i].start();
}
for (int i = 0; i < threads.length; i++) {
    threads[i].join();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21