AtomicInteger
是 Java 并发包 java.util.concurrent.atomic
中的一个类,它提供了对 int
类型变量的原子操作。通过使用底层的硬件原语(如CAS操作),AtomicInteger
能够保证对其操作的线程安全性,而不需要显式使用锁。
CAS 操作
CAS(Compare-And-Swap)是一种原子操作,它包含三个操作数——内存地址、预期值和新值。CAS 操作会比较内存地址中的当前值与预期值,如果两者相等,则将该位置更新为新值;否则,不进行任何操作。整个过程是不可中断的,因此能够保证线程安全。
底层实现
1. 成员变量和构造方法
java
代码解读
复制代码
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
java
代码解读
复制代码
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
compareAndSet
方法直接调用了 Unsafe
的 compareAndSwapInt
方法,这个方法可以保证在硬件层面上的原子性操作。
3. 常用方法
get 和 set
整理了一份好像面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记【点击此处即可】免费获取
java
代码解读
复制代码
public final int get() { return value; } public final void set(int newValue) { value = newValue; }
这些方法分别用于获取和设置当前值,其中 get
方法确保返回最新的值,而 set
方法则直接将新值赋给 value
。
incrementAndGet 和 decrementAndGet
java
代码解读
复制代码
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
java
代码解读
复制代码
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
方法的定义:
java
代码解读
复制代码
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
:
c++
代码解读
复制代码
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 操作。相应的汇编指令可能类似这样:
ini
代码解读
复制代码
mov eax, expected ; 将预期值加载到EAX寄存器中 mov ebx, new_value ; 将新值加载到EBX寄存器中 lock cmpxchg [addr], ebx ; 尝试将EBX的值交换到[addr]位置,如果EAX与[addr]内容相同
汇编级别的细节
以下是一个简化的示例,展示了 x86 汇编中的 CMPXCHG
指令:
css
代码解读
复制代码
; 输入: ; 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
在高并发环境下比传统的同步机制更高效。