欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中的 AtomicReference類概覽及實現(xiàn)方案

 更新時間:2025年09月15日 12:01:39   作者:探索java  
在Java開發(fā)中,常常需要構(gòu)建無鎖的應(yīng)用程序,以實現(xiàn)高性能的并發(fā)控制,本文給大家介紹Java中的AtomicReference類概覽及實現(xiàn)方案,感興趣的朋友跟隨小編一起看看吧

一、引言

在 Java 開發(fā)中,常常需要構(gòu)建無鎖的應(yīng)用程序,以實現(xiàn)高性能的并發(fā)控制。AtomicReference 就是它們中極其重要的一員。作為一種基于 CAS 機制實現(xiàn)的原實性工具,它在構(gòu)建無鎖隊列、單例、上下文切換等場景中有著重要地位。

本文將隨著一個體系化的進程,從基本概念到源碼分析,展示 AtomicReference 在實際中如何做到 "精精不苦無鎖同步"。

二、AtomicReference 概覽

2.1 概念介紹

AtomicReference 是一個對應(yīng)任意對象的原實性工具類,繼承自 java.util.concurrent.atomic.AtomicReference<T>,提供類似于基本型的 atomicXXX 類,但對象化。

它內(nèi)部基于 Unsafe 的 CAS 操作,能夠精確地實現(xiàn)與目標對象的引用替換。

2.2 類結(jié)構(gòu)快覽

public class AtomicReference<V> implements java.io.Serializable {
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;
    private volatile V value;
    // ... 重點方法看后續(xù)
}

類中主要是一個 volatile 字段 value,和基于 Unsafe 的 CAS 操作支持。

三、核心概念

3.1 CAS 原理

CAS(Compare-And-Swap)是一種無鎖的原實性操作機制,其核心是把當前值和預期值比較,如果相等,則更新為新值。

實現(xiàn)簡化為:

boolean compareAndSet(V expect, V update) {
    if (value == expect) {
        value = update;
        return true;
    } else {
        return false;
    }
}

實際中這個操作是依賴硬件持有的原實性 CPU 指令來完成的,在 Java 中通過 Unsafe 來調(diào)用。

3.2 Unsafe 類

Unsafe 是一個本地級類,提供相當不安全的內(nèi)核操作,包括直接操作內(nèi)存,操作類的內(nèi)部字段值等。

四、AtomicReference 的基礎(chǔ)用法

4.1 創(chuàng)建和基本操作

public class AtomicReferenceDemo {
    public static void main(String[] args) {
        AtomicReference<String> ref = new AtomicReference<>("initial");
        boolean updated = ref.compareAndSet("initial", "updated");
        System.out.println("Update successful: " + updated);
        System.out.println("Current value: " + ref.get());
    }
}

輸出:

Update successful: true
Current value: updated

五、源碼深度解析

在深入理解 AtomicReference 的行為機制之前,有必要從源碼級別進行一層層剖析。本章節(jié)將覆蓋類結(jié)構(gòu)、關(guān)鍵方法、內(nèi)存語義以及 Unsafe.compareAndSwapObject 方法的底層實現(xiàn)。

5.1 類定義與字段分析

源碼路徑:java.util.concurrent.atomic.AtomicReference

public class AtomicReference<V> implements java.io.Serializable {
    private static final long serialVersionUID = -1848883965231344442L;
    // Unsafe 是用于執(zhí)行底層 CAS 操作的核心類
    private static final sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
    // 用于存儲 value 字段的偏移量,在初始化時通過反射獲取
    private static final long valueOffset;
    // 關(guān)鍵字段,volatile 保證可見性
    private volatile V value;
    static {
        try {
            // valueOffset 的計算,核心反射操作
            valueOffset = unsafe.objectFieldOffset
                (AtomicReference.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
    public AtomicReference(V initialValue) {
        value = initialValue;
    }
    public AtomicReference() {
    }

? 說明:

  • value 是被保護的實際引用,必須是 volatile,以確保在并發(fā)線程之間可見。
  • valueOffset 是通過反射拿到的 value 字段在對象內(nèi)存結(jié)構(gòu)中的偏移量。
  • 所有 CAS 操作都基于 valueOffset。

5.2 核心方法解析

public final boolean compareAndSet(V expect, V update) {
    return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}
public final void set(V newValue) {
    value = newValue;
}
public final void lazySet(V newValue) {
    unsafe.putOrderedObject(this, valueOffset, newValue);
}
public final V get() {
    return value;
}
public final V getAndSet(V newValue) {
    while (true) {
        V current = get();
        if (compareAndSet(current, newValue))
            return current;
    }
}

方法說明:

  • compareAndSet: 基于 CAS 的原子條件更新,是核心方法。
  • set: 普通賦值操作,具備 volatile 語義。
  • lazySet: 有序?qū)懭耄m用于延遲可見的情況,性能更優(yōu)。
  • getAndSet: 實現(xiàn)方式為循環(huán) CAS。

5.3 內(nèi)存語義分析

  • volatile 語義:保證讀寫操作的可見性,防止指令重排。
  • compareAndSet 的語義:具備 volatile 的讀+寫語義,同時含有內(nèi)存屏障(full fence)。
  • lazySet 語義:具備“store-store barrier”,只保證新值最終會被線程看到,但不強制立即生效。

內(nèi)存屏障說明:

compareAndSet:
  LoadLoad + LoadStore + StoreStore + StoreLoad(全屏障)
lazySet:
  StoreStore(寫屏障)

這也是為什么在高并發(fā)但不要求嚴格即時可見性的場景下,lazySet 更高效。

5.4 Unsafe.compareAndSwapObject 深度剖析

該方法定義如下:

public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object x);

其是 native 方法,最終調(diào)用 JVM 內(nèi)部封裝的 CPU 指令實現(xiàn)(如 x86 架構(gòu)下的 CMPXCHG)。其關(guān)鍵邏輯:

  • 比較對象 o 中偏移量為 offset 的字段值是否等于 expected
  • 如果相等,則用 x 替換原值,返回 true
  • 否則不做修改,返回 false

?? 實質(zhì):此操作具備原子性,不會被線程上下文切換打斷。

CPU 層級說明(以 x86 為例)

在底層,JVM 會借助 CPU 提供的原子指令實現(xiàn)上述邏輯,比如:

lock cmpxchg r/m32, r32  // 帶鎖的比較交換,保證總線級別原子

這確保了在多核環(huán)境中即使多個線程同時競爭更新,依然可以避免數(shù)據(jù)競爭。

5.5 JVM 字節(jié)碼觀察

我們通過 javap -c -v 查看 compareAndSet 調(diào)用層次:

javap -c -v java.util.concurrent.atomic.AtomicReference

輸出片段如下:

  public final boolean compareAndSet(java.lang.Object, java.lang.Object);
    Code:
       0: getstatic     #16                 // Field unsafe:Lsun/misc/Unsafe;
       3: aload_0
       4: getstatic     #18                 // Field valueOffset:J
       7: aload_1
       8: aload_2
       9: invokevirtual #24                 // Method sun/misc/Unsafe.compareAndSwapObject
      12: ireturn

從字節(jié)碼角度也能看出 compareAndSet 調(diào)用是對 Unsafe 對象方法的直接轉(zhuǎn)發(fā)。

小結(jié)

  • AtomicReference 實現(xiàn)基于 Unsafe 的 CAS 操作,繞過 synchronized 實現(xiàn)高性能原子更新。
  • valueOffset 是通過反射獲取的內(nèi)存偏移,用于精確定位對象字段。
  • compareAndSet 調(diào)用的是 JVM 層原子指令(cmpxchg),確保并發(fā)安全。
  • volatilelazySet 提供不同程度的內(nèi)存可見性策略,需根據(jù)具體業(yè)務(wù)場景選擇。

六、常見問題及解決方案

AtomicReference 雖然為無鎖編程提供了便捷手段,但在實踐中仍然存在若干值得注意的問題。特別是在高并發(fā)環(huán)境下,如果理解不當,可能會引入隱蔽的并發(fā) bug。本章節(jié)總結(jié)了實際使用中遇到的典型問題及對應(yīng)解決策略。

6.1 問題一:ABA 問題

問題描述:

CAS 操作基于對象的引用地址進行比較,若地址值相同則視為“未變”,這就帶來一個典型并發(fā)陷阱:ABA 問題。

定義:

線程 A 讀取變量為 A,線程 B 將其從 A 改為 B 再改回 A,線程 A 發(fā)現(xiàn)當前還是 A,于是操作成功,但其實已經(jīng)發(fā)生過變化。

示例:

AtomicReference<String> ref = new AtomicReference<>("A");
Thread t1 = new Thread(() -> {
    String prev = ref.get(); // 讀取到 A
    sleep(100);
    boolean success = ref.compareAndSet(prev, "C");
    System.out.println("T1 CAS: " + success); // 可能為 true,但實際中間已經(jīng)被改動過
});
Thread t2 = new Thread(() -> {
    ref.compareAndSet("A", "B");
    ref.compareAndSet("B", "A");
});
t1.start();
t2.start();

風險:

雖然最終值為 "A",但實際上經(jīng)歷了變化。CAS 操作未感知這些中間變更,可能導致數(shù)據(jù)一致性問題。

解決方案:AtomicStampedReference

為了解決 ABA 問題,JDK 提供了 AtomicStampedReference 類。它不僅保存值,還維護一個版本號(stamp),每次更新都需同時更新 stamp,從而感知變量是否真正變化過。

示例:

AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0);
Thread t1 = new Thread(() -> {
    int[] stamp = new int[1];
    String prev = ref.get(stamp);
    sleep(100);
    boolean success = ref.compareAndSet(prev, "C", stamp[0], stamp[0] + 1);
    System.out.println("T1 CAS with stamp: " + success);
});
Thread t2 = new Thread(() -> {
    int[] stamp = new int[1];
    String curr = ref.get(stamp);
    ref.compareAndSet(curr, "B", stamp[0], stamp[0] + 1);
    ref.compareAndSet("B", "A", stamp[0] + 1, stamp[0] + 2);
});
t1.start();
t2.start();

? 由于 stamp 不同,即使值回到 "A",也能檢測到版本不一致,防止 ABA 問題。

6.2 問題二:引用本身原子,不代表對象狀態(tài)原子

AtomicReference<T> 僅保證對象引用的更新是原子的,并不能保證引用對象內(nèi)部的字段或狀態(tài)是線程安全的。

示例:

class User {
    String name;
    int age;
}
AtomicReference<User> ref = new AtomicReference<>(new User());
ref.get().age++; // 非原子操作

風險:

如果多個線程同時修改 ref.get().age++,依然可能引發(fā)競態(tài)條件。

解決方案:

  • 使用 AtomicReference<User> 實現(xiàn)整體替換(不可變類方案)
  • 或封裝更新邏輯,通過 CAS 替換整個對象:
while (true) {
    User oldUser = ref.get();
    User newUser = new User();
    newUser.name = oldUser.name;
    newUser.age = oldUser.age + 1;
    if (ref.compareAndSet(oldUser, newUser)) break;
}

6.3 問題三:頻繁 CAS 導致性能下降

CAS 是樂觀鎖思想,但在高競爭場景下,多次失敗會導致性能問題。

解決策略:

  • 使用 LongAdder、StampedLock 等替代方案
  • 或者使用回退策略、自旋上限等優(yōu)化手段

小結(jié)

問題現(xiàn)象解決方式
ABA 問題值恢復原樣但中途被修改使用 AtomicStampedReference
引用更新原子但狀態(tài)不安全內(nèi)部字段可能并發(fā)讀寫沖突使用不可變對象整體替換
CAS 自旋性能差多線程競爭失敗導致 CPU 消耗上升增加退避策略 / 限制自旋次數(shù)

在實際應(yīng)用中,合理選擇原子類并避免過度依賴單一機制是保障系統(tǒng)健壯性的關(guān)鍵。

到此這篇關(guān)于Java 中的 AtomicReference 類及其實現(xiàn)的文章就介紹到這了,更多相關(guān)Java AtomicReference 類內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot中使用websocket出現(xiàn)404的解決方法

    SpringBoot中使用websocket出現(xiàn)404的解決方法

    在Springboot中使用websocket時,本地開發(fā)環(huán)境可以正常運行,但部署到服務(wù)器環(huán)境出現(xiàn)404問題,所以本文小編講給大家詳細介紹一下SpringBoot中使用websocket出現(xiàn)404的解決方法,需要的朋友可以參考下
    2023-09-09
  • RocketMQ順序消息的原理與特點

    RocketMQ順序消息的原理與特點

    RocketMQ作為一款純java、分布式、隊列模型的開源消息中間件,支持事務(wù)消息、順序消息、批量消息、定時消息、消息回溯等,本篇我們了解如何實現(xiàn)順序消息的原理與特點
    2023-02-02
  • Java?static關(guān)鍵字詳細解析

    Java?static關(guān)鍵字詳細解析

    這篇文章主要介紹了Java?static關(guān)鍵字詳細解析,java中的static關(guān)鍵字主要用于內(nèi)存管理,可以用在變量、方法、代碼塊和嵌套類中。更多相關(guān)介紹,需要的小伙伴可以參考一下
    2022-08-08
  • 基于Nacos實現(xiàn)Spring Cloud Gateway實現(xiàn)動態(tài)路由的方法

    基于Nacos實現(xiàn)Spring Cloud Gateway實現(xiàn)動態(tài)路由的方法

    這篇文章主要介紹了基于Nacos實現(xiàn)Spring Cloud Gateway實現(xiàn)動態(tài)路由的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-06-06
  • Spring boot 跳轉(zhuǎn)到j(luò)sp頁面的實現(xiàn)方法

    Spring boot 跳轉(zhuǎn)到j(luò)sp頁面的實現(xiàn)方法

    本篇文章主要介紹了Spring boot 跳轉(zhuǎn)到j(luò)sp頁面的實現(xiàn)方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • java使用itext導出PDF文本絕對定位(實現(xiàn)方法)

    java使用itext導出PDF文本絕對定位(實現(xiàn)方法)

    下面小編就為大家?guī)硪黄猨ava使用itext導出PDF文本絕對定位(實現(xiàn)方法)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • Spring學習教程之AOP模塊的概述

    Spring學習教程之AOP模塊的概述

    AOP 從功能的角度來講,可能看作OOP編程方式的一種補充,提供了一種不同的代碼或者系統(tǒng)組織方式,下面這篇文章主要給大家介紹了關(guān)于Spring學習教程之AOP模塊的相關(guān)資料,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2018-05-05
  • JVM教程之內(nèi)存管理和垃圾回收(三)

    JVM教程之內(nèi)存管理和垃圾回收(三)

    這篇文章主要介紹了JVM學習筆記的第三篇內(nèi)存管理和垃圾回收,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • Java后臺Controller實現(xiàn)文件下載操作

    Java后臺Controller實現(xiàn)文件下載操作

    這篇文章主要介紹了Java后臺Controller實現(xiàn)文件下載操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • springboot swagger 接口文檔分組展示功能實現(xiàn)

    springboot swagger 接口文檔分組展示功能實現(xiàn)

    這篇文章主要介紹了springboot swagger 接口文檔分組展示功能實現(xiàn),本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧
    2024-03-03

最新評論