切换语言为:繁体

详细介绍Java的AtomicInteger是如何保证线程安全

  • 爱糖宝
  • 2024-08-23
  • 2054
  • 0
  • 0

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 方法直接调用了 UnsafecompareAndSwapInt 方法,这个方法可以保证在硬件层面上的原子性操作。

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 在高并发环境下比传统的同步机制更高效。

0条评论

您的电子邮件等信息不会被公开,以下所有项均必填

OK! You can skip this field.