# AtomicInteger
AtomicInteger是java.util.concurrent.atomic包下提供的原子计数类,能够实现并发环境下的线程安全的int数据的读写、原子修改等操作,解决经典的i++的线程不安全问题。
使用场景
- 计数,保证原子性可见性防止重排序等,例如AQS中的state字段,和AtomicInteger是比较类似的,不过AQS为了提升性能直接通过Unsafe操作volatile变量
- 自旋锁,通过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;
}
2
3
4
5
6
7
8
incrementAndGet实现,incrementAndGet是原子加1后获取加之后的值,直接调用的Unsafe的getAndAddInt方法实现
public final int incrementAndGet() {
return U.getAndAddInt(this, VALUE, 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);
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();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21