AtomicInteger 是 Java 并发包 java.util.concurrent.atomic 中的一个类,它提供了对 int 类型变量的原子操作。通过使用底层的硬件原语(如CAS操作),AtomicInteger 能够保证对其操作的线程安全性,而不需要显式使用锁。
CAS 操作
CAS(Compare-And-Swap)是一种原子操作,它包含三个操作数——内存地址、预期值和新值。CAS 操作会比较内存地址中的当前值与预期值,如果两者相等,则将该位置更新为新值;否则,不进行任何操作。整个过程是不可中断的,因此能够保证线程安全。
底层实现
1. 成员变量和构造方法
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// Unsafe 实例,用于执行底层的CAS操作
private static final Unsafe unsafe = Unsafe.getUnsafe();
// value 字段在内存中的偏移量
private static final long valueOffset;
// 初始化 value 字段的内存偏移量
static {
try {
valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) {
throw new Error(ex);
}
}
// volatile 修饰的 int 值,确保对该值的修改对所有线程可见
private volatile int value;
public AtomicInteger(int initialValue) {
value = initialValue;
}
public AtomicInteger() {
}
}
这里,我们看到 AtomicInteger 使用了 sun.misc.Unsafe 类来进行低级别的原子操作。此外,value 字段被 volatile 修饰,以确保其修改对所有线程立即可见。
2. 核心方法:compareAndSet
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
compareAndSet 方法直接调用了 Unsafe 的 compareAndSwapInt 方法,这个方法可以保证在硬件层面上的原子性操作。
3. 常用方法
get 和 set
public final int get() {
return value;
}
public final void set(int newValue) {
value = newValue;
}
这些方法分别用于获取和设置当前值,其中 get 方法确保返回最新的值,而 set 方法则直接将新值赋给 value。
incrementAndGet 和 decrementAndGet
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
public final int decrementAndGet() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return next;
}
}
这些方法通过循环和 CAS 操作来实现自增和自减功能。如果 compareAndSet 操作失败(说明有其他线程修改了 value),则重新读取当前值并重试,直到成功为止。
addAndGet 和 getAndAdd
public final int addAndGet(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return next;
}
}
public final int getAndAdd(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return current;
}
}
这两个方法实现了加法操作,并返回相应的结果。addAndGet 返回的是加法操作后的新值,而 getAndAdd 返回的是加法操作前的旧值。
4. Unsafe 类的使用
AtomicInteger 依赖于 Unsafe 类来实现底层的原子操作。Unsafe 类提供了一组硬件级别的操作,可以直接操作内存和对象。以下是 compareAndSwapInt 方法的定义:
public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);
这个方法是一个本地方法,通过JNI(Java Native Interface)调用底层的硬件指令来实现原子性操作。
5. compareAndSwapInt 分析
此方法的具体实现是在 JVM 的本地代码中完成的,这通常是用 C 或 C++ 编写的。
底层实现
compareAndSwapInt 的具体实现因 JVM 的实现和运行的平台不同而有所差异。以 OpenJDK 为例,在 HotSpot JVM 中,compareAndSwapInt 的实现可以在以下文件中找到:
- 对于64位架构:
src/hotspot/share/runtime/atomic.cpp - 对于32位架构:
src/hotspot/os_cpu/linux_x86/atomic_linux_x86.inline.hpp
基于x86架构的CAS操作
以 x86 架构为例,底层硬件指令 CMPXCHG(比较并交换)用于实现 CAS 操作。以下是一个伪代码示例,展示了如何在 C++ 中实现 compareAndSwapInt:
inline bool compareAndSwapInt(jobject obj, jlong offset, jint expected, jint x) {
jint* addr = (jint*)((char*)obj + offset); // 计算对象字段的真实地址
return __sync_bool_compare_and_swap(addr, expected, x); // 使用 GCC 提供的内置函数
}
在这个伪代码中,__sync_bool_compare_and_swap 是 GCC 提供的内置函数,它生成适当的机器指令来实现 CAS 操作。相应的汇编指令可能类似这样:
mov eax, expected ; 将预期值加载到EAX寄存器中
mov ebx, new_value ; 将新值加载到EBX寄存器中
lock cmpxchg [addr], ebx ; 尝试将EBX的值交换到[addr]位置,如果EAX与[addr]内容相同
汇编级别的细节
以下是一个简化的示例,展示了 x86 汇编中的 CMPXCHG 指令:
; 输入:
; EAX: 预期值
; EBX: 新值
; [ECX]: 内存地址
lock cmpxchg [ecx], ebx ; 如果 [ecx] == eax,则 [ecx] = ebx,否则 eax = [ecx]
如果内存地址 ecx 中的值与 eax 中的值相等,那么将 ebx 中的值写入 ecx 指向的内存地址;否则,将 ecx 指向的内存地址中的值加载到 eax 中。整个操作在硬件层面上是原子的。
总结
AtomicInteger 通过 Unsafe 类提供的原子性操作实现了线程安全的整数操作。它使用 volatile 保证内存可见性,通过 CAS 操作(如 compareAndSet)实现无锁的线程安全。这使得 AtomicInteger 在高并发环境下比传统的同步机制更高效。