首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

AtomicIntegerFieldUpdater:讓普通變數也享受原子操作的利器

     編輯:Java 綜合教學

AtomicIntegerFieldUpdater:讓普通變數也享受原子操作的利器

問題背景

專案演進中,原本非執行緒安全的 volatile int 欄位突然面臨併發寫入需求。若逐一修改所有存取點,違反開閉原則(對擴充開放、對修改關閉),且工作量大、風險高。特別是許多場景僅為唯讀,無需同步。

解決方案:原子欄位更新器

java.util.concurrent.atomic 提供三種更新器,無需修改原欄位宣告,透過反射直接對實例欄位進行 CAS 操作:

  • AtomicIntegerFieldUpdater - int 欄位
  • AtomicLongFieldUpdater - long 欄位
  • AtomicReferenceFieldUpdater - 物件參考欄位

實戰範例:選舉計票系統

public class AtomicIntegerFieldUpdaterDemo {
    public static class Candidate {
        int id;
        volatile int score;  // 必須 volatile
    }

    // 建立更新器:目標類別、欄位名稱
    public static final AtomicIntegerFieldUpdater<Candidate> scoreUpdater =
        AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score");

    // 基準計數器(AtomicInteger 本身線程安全)
    public static final AtomicInteger allScore = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        final Candidate candidate = new Candidate();
        Thread[] threads = new Thread[10000];

        // 模擬 10000 選民,60% 投贊成票
        for (int i = 0; i < 10000; i++) {
            threads[i] = new Thread(() -> {
                if (Math.random() > 0.4) {
                    scoreUpdater.incrementAndGet(candidate);  // 原子增
                    allScore.incrementAndGet();
                }
            });
            threads[i].start();
        }

        // 等待所有執行緒完成
        for (Thread t : threads) { t.join(); }

        System.out.println("score = " + candidate.score);
        System.out.println("allScore = " + allScore.get());
        // 輸出兩者相等,證明線程安全
    }
}

執行結果驗證

score = 5987
allScore = 5987

多次執行結果始終一致,證明 AtomicIntegerFieldUpdater 成功保障了普通 volatile int 欄位的原子性更新。

三大使用限制(必讀)

限制說明原因
可見性 欄位必須為更新器可見 內部用反射 Field.getDeclaredField(),private 欄位會失敗
volatile 欄位必須宣告為 volatile 確保讀取可見性,CAS 需要最新值
非 static 不支援 static 欄位 Unsafe.objectFieldOffset() 僅支援實例欄位

適用場景判斷

  • ✅ 既有 volatile 欄位需新增原子寫入
  • ✅ 存取點分散,不想大範圍重構
  • ✅ 部分場景唯讀,部分需原子更新
  • ❌ 新設計欄位 → 直接用 AtomicInteger 更簡潔
  • ❌ static 欄位 → 需其他方案

原理簡析

更新器建立時透過反射獲取 Field,呼叫 Unsafe.objectFieldOffset(field) 取得欄位在物件中的記憶體偏移量。後續 CAS 操作直接對該記憶體位址進行比較交換,繞過 Java 存取檢查,實現無鎖原子更新。

來源:《實戰 Java 高併發程式設計》

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved