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

Java 並發核心技術:CAS 原理、Atomic 類實現與無鎖編程實戰

     編輯:Java 綜合教學

Java 並發核心技術:CAS 深度解析與無鎖編程實戰

CAS (Compare-And-Swap,比較並交換) 是現代並發編程的基石,Java 5+ 引入 java.util.concurrent.atomic 包將處理器級 CAS 指令暴露給開發者,實現了無鎖線程安全。本文將從原理、場景、源碼、實戰四個維度深度剖析 CAS 技術。

CAS 核心原理

三個操作數:

  • V:內存地址存儲的當前值
  • A:線程預期的舊值 (期望值)
  • B:要設置的新值

原子語義:if (V == A) { V = B; return true; } else { return false; } — 整個判斷與更新過程不可分割,由 CPU 硬體指令保證 (x86: LOCK CMPXCHG,ARM: LDREX/STREX)。

經典場景:Check-Then-Act 竞態條件

// 非線程安全:檢查與操作非原子
class MyLock {
    private boolean locked = false;
    public boolean lock() {
        if (!locked) { locked = true; return true; } // 競態窗口!
        return false;
    }
}

多線程並發時,線程 A、B 同時讀到 locked=false,雙雙進入 if 設置為 true,導致鎖失效。

傳統方案:synchronized 互斥鎖

public synchronized boolean lock() { // 重量級鎖,上下文切換開銷大
    if (!locked) { locked = true; return true; }
    return false;
}

雖然解決線程安全,但引入悲觀鎖機制:線程阻塞、內核態切換、優先級反轉等性能隱患。

CAS 無鎖方案:AtomicBoolean 實戰

import java.util.concurrent.atomic.AtomicBoolean;

class MyLock {
    private final AtomicBoolean locked = new AtomicBoolean(false);
    
    public boolean lock() {
        // compareAndSet(expected, update) 返回 boolean
        return locked.compareAndSet(false, true); // 原子 CAS 操作
    }
    
    public void unlock() {
        locked.set(false); // volatile 寫入,保證可見性
    }
}

優勢:無線程阻塞、無上下文切換、用戶態完成同步、性能隨併發度線性擴展 (低競爭場景)。

JDK 源碼級實現剖析

AtomicInteger.incrementAndGet() 調用鏈

// AtomicInteger
public final int incrementAndGet() {
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}

// Unsafe (JVM 內部實現)
public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    do {
        v = getIntVolatile(o, offset); // volatile 讀取最新值
    } while (!compareAndSwapInt(o, offset, v, v + delta)); // CAS 自旋重試
    return v;
}
  • valueOffset:字段在對象內存中的偏移量 (對象頭 + 實例字段佈局)
  • getIntVolatile:內存屏障語義,禁止指令重排,強制從主內存讀取
  • compareAndSwapInt:JVM 內部函數,映射為 CPU LOCK CMPXCHG 指令

CAS 三大核心問題與解決方案

問題描述解決方案
ABA 問題 值 A→B→A,CAS 誤以為未變更 AtomicStampedReference / AtomicMarkableReference 引入版本戳/標記位
自旋 CPU 佔用高 長期 CAS 失敗佔用 CPU JDK 8+ LongAdder 分段計數器;適度 Thread.yield() / LockSupport.parkNanos()
只能保證單變量原子 多變量組合操作非原子 封裝為單一對象用 AtomicReference;或用鎖/STM (Software Transactional Memory)

實戰:高性能無鎖計數器對比

public class CounterBenchmark {
    static final int THREADS = 16;
    static final int ITERATIONS = 1_000_000;
    
    public static void main(String[] args) throws InterruptedException {
        // 1. synchronized
        testSync(() -> new SyncCounter());
        // 2. ReentrantLock
        testSync(() -> new LockCounter());
        // 3. AtomicLong
        testSync(() -> new AtomicCounter());
        // 4. LongAdder (JDK 8+ 推薦高併發)
        testSync(() -> new LongAdderCounter());
    }
    
    static void testSync(Supplier supplier) throws InterruptedException {
        Counter c = supplier.get();
        long start = System.nanoTime();
        ExecutorService es = Executors.newFixedThreadPool(THREADS);
        for (int i = 0; i < THREADS; i++) 
            es.execute(() -> { for (int j = 0; j < ITERATIONS; j++) c.increment(); });
        es.shutdown(); es.awaitTermination(1, TimeUnit.MINUTES);
        System.out.printf("%s: %,d ns, value=%,d%n", c.getClass().getSimpleName(), 
            System.nanoTime() - start, c.get());
    }
}

interface Counter { void increment(); long get(); }
class SyncCounter implements Counter { private long v; public synchronized void increment() { v++; } public long get() { return v; } }
class LockCounter implements Counter { private long v; private final Lock l = new ReentrantLock(); public void increment() { l.lock(); try { v++; } finally { l.unlock(); } } public long get() { return v; } }
class AtomicCounter implements Counter { private final AtomicLong v = new AtomicLong(); public void increment() { v.incrementAndGet(); } public long get() { return v.get(); } }
class LongAdderCounter implements Counter { private final LongAdder v = new LongAdder(); public void increment() { v.increment(); } public long get() { return v.sum(); } }

典型輸出 (高併發場景):

SyncCounter:      1,200,000,000 ns, value=16,000,000
LockCounter:      1,150,000,000 ns, value=16,000,000
AtomicCounter:      450,000,000 ns, value=16,000,000
LongAdderCounter:   120,000,000 ns, value=16,000,000  ← 分段無鎖,性能碾壓

CAS 應用邊界與最佳實踐

  • 適用:計數器、開關狀態、序列號生成、無鎖隊列節點鏈接、單一對象引用更新
  • 不適用:複雜業務邏輯多變量關聯、長鏈路操作、需要事務語義場景
  • 選型建議:
    • JDK 8+ 高併發計數 → LongAdder/DoubleAdder
    • 需版本控制 → AtomicStampedReference
    • 布爾標誌 → AtomicBoolean
    • 對象引用原子更新 → AtomicReference

總結

CAS 以樂觀鎖思想配合硬體原子指令,實現了用戶態無阻塞同步,是 JUC 包高性能並發組件的基石。理解 Unsafe、volatile 內存語義、ABA 問題與 LongAdder 分段思想,能幫助開發者在高併發系統中做出正確的同步策略選型。實際開發中,優先考慮成熟組件 (ConcurrentHashMap、LongAdder、CompletableFuture) 而非手寫 CAS 邏輯,除非在構建基礎設施級組件。

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