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

Java 並發控制機制全景圖:從 synchronized 到無鎖編程完整指南

     編輯:Java 相關知識

Java 內存模型(JMM)基石

  • 主內存:共享變量存儲區(堆)
  • 工作內存:每線程私有,持有共享變量副本(CPU 寄存器、L1/L2 Cache)
  • 核心規則:線程操作副本,需通過 鎖 或 volatile 同步回主內存
  • 三大特性:原子性、可見性、有序性

同步工具箱完整對比

工具層級核心特性適用場景效能
volatile關鍵字可見性 + 禁止指令重排,無原子性狀態標記、雙重檢查鎖、單次寫入多讀極高(無鎖)
synchronized關鍵字/監視器可重入、公平性弱、JVM 優化深(偏向鎖、輕量級鎖、膨脹)一般同步、鎖競爭不激烈、代碼簡潔優先中(JDK 6+ 優化後接近 Lock)
ReentrantLockAPI (AQS)可中斷、可超時、公平/非公平、條件變量高並發、複雜同步邏輯、需中斷/超時控制高(非公平模式)
ReentrantReadWriteLockAPI (AQS)讀共享/寫獨享、降級支援、可重入讀多寫少、緩存、配置熱加載高(讀並行)
StampedLockAPI (JDK 8)樂觀讀、無條件變量、不可重入極高讀並發、無寫入衝突場景極高
SemaphoreAPI (AQS)計數許可證、公平/非公平資源池限流、連接池、並發任務控制中
CountDownLatchAPI (AQS)一次性倒計時門闂多線程啟動同步、分階段等待高
CyclicBarrierAPI (AQS)可重置循環屏障、可選屏障動作並行計算分階段匯總、遊戲關卡同步中
CompletableFutureAPI (JDK 8)非阻塞組合、異步流水線、異常傳播異步編排、多服務聚合、響應式編程高

核心組件深度解析

synchronized 進階用法


// 1. 同步代碼塊:縮小鎖粒度
public void transfer(Account from, Account to, int amount) {
    Object lock1 = from.hashCode() < to.hashCode() ? from : to;
    Object lock2 = from.hashCode() < to.hashCode() ? to : from;
    synchronized (lock1) {
        synchronized (lock2) { // 固定順序防死鎖
            from.withdraw(amount);
            to.deposit(amount);
        }
    }
}

// 2. 靜態方法鎖住 Class 對象
public static synchronized void staticMethod() { }

// 3. wait/notify 經典模式:生產者消費者
synchronized (queue) {
    while (queue.isEmpty()) queue.wait(); // 必須在循環中檢查條件
    Item item = queue.poll();
    queue.notifyAll(); // 喚醒所有等待者
}

ReentrantLock 最佳實踐


private final Lock lock = new ReentrantLock(); // 非公平鎖,效能優

public void doWork() {
    if (lock.tryLock(5, TimeUnit.SECONDS)) { // 帶超時,防死等
        try {
            // 關鍵區段
        } finally {
            lock.unlock(); // 必須在 finally 中釋放
        }
    } else {
        log.warn("獲取鎖超時");
    }
}

// 可中斷鎖:響應線程中斷
public void interruptibleWork() throws InterruptedException {
    lock.lockInterruptibly();
    try { /* ... */ } finally { lock.unlock(); }
}

Condition 精確控制


class BoundedQueue<E> {
    private final Lock lock = new ReentrantLock();
    private final Condition notEmpty = lock.newCondition();
    private final Condition notFull = lock.newCondition();
    private final Queue<E> queue = new ArrayDeque<>();
    private final int capacity;

    public void put(E e) throws InterruptedException {
        lock.lockInterruptibly();
        try {
            while (queue.size() == capacity) notFull.await();
            queue.add(e);
            notEmpty.signal(); // 喚醒一個消費者
        } finally { lock.unlock(); }
    }

    public E take() throws InterruptedException {
        lock.lockInterruptibly();
        try {
            while (queue.isEmpty()) notEmpty.await();
            E e = queue.poll();
            notFull.signal(); // 喚醒一個生產者
            return e;
        } finally { lock.unlock(); }
    }
}

Semaphore 資源池控制


class ResourcePool<T> {
    private final Semaphore available;
    private final Queue<T> pool = new ConcurrentLinkedQueue<>();

    public ResourcePool(int maxSize, Supplier<T> factory) {
        available = new Semaphore(maxSize, true);
        for (int i = 0; i < maxSize; i++) pool.add(factory.get());
    }

    public T acquire() throws InterruptedException {
        available.acquire(); // 獲取許可
        T resource = pool.poll();
        if (resource == null) resource = createNew(); // 池空時動態創建
        return resource;
    }

    public void release(T resource) {
        pool.add(resource);
        available.release(); // 釋放許可
    }
}

鎖優化五大原則

  1. 縮小鎖持有時間:只同步必要代碼,非關鍵操作移出同步塊
  2. 降低鎖粒度:分段鎖、分桶鎖(如 `ConcurrentHashMap` Segment/CAS)
  3. 鎖分離:讀寫鎖、雙隊列鎖(`LinkedBlockingQueue` takeLock/putLock)
  4. 鎖粗化:合併連續同步塊,減少鎖獲取/釋放開銷(JVM 自動優化)
  5. 消除鎖:ThreadLocal、CAS、不可變對象、無鎖數據結構

無鎖編程:CAS 與原子類

CAS 原理

CAS(V, E, N):比較並交換。僅當內存值 V 等於預期值 E 時,原子更新為新值 N。失敗不阻塞,重試或放棄。

AtomicInteger 效能實測


// 100 萬次計數,3 線程
AtomicInteger atomic = new AtomicInteger(0);
// vs
synchronized int count = 0;

// 結果:Atomic ~45ms,synchronized ~200ms(4-5 倍差距)
// 原因:CAS 用戶態完成,無內核態切換、無線程掛起/喚醒

常用原子類

  • 基本型別:AtomicInteger、AtomicLong、AtomicBoolean、AtomicReference
  • 陣列:AtomicIntegerArray、AtomicReferenceArray
  • 字段更新器:AtomicIntegerFieldUpdater、AtomicReferenceFieldUpdater(反射式,無需修改類)
  • 累加器:LongAdder、DoubleAdder(JDK 8,高併發計數器,分段 CAS,效能碾壓 AtomicLong)

ThreadLocal 線程封閉


// 請求上下文傳遞
public class RequestContext {
    private static final ThreadLocal<Context> HOLDER = new ThreadLocal<>();
    public static void set(Context ctx) { HOLDER.set(ctx); }
    public static Context get() { return HOLDER.get(); }
    public static void clear() { HOLDER.remove(); } // 必須清理,防內存洩漏
}

// SimpleDateFormat 非線程安全,ThreadLocal 獨享
private static final ThreadLocal<DateFormat> DATE_FORMAT = ThreadLocal.withInitial(
    () -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
);

注意:線程池場景必須在任務結束時 `remove()`,否則上下文洩漏到下一任務。

實戰選型決策樹

  1. 是否可用不可變對象或線程封閉?是 → 無鎖最優
  2. 是否為計數/累加?是 → LongAdder/AtomicLong
  3. 是否為單次寫入、多次讀取?是 → volatile 或 StampedLock 樂觀讀
  4. 是否為讀多寫少?是 → ReentrantReadWriteLock 或 StampedLock
  5. 是否需要中斷/超時/條件變量?是 → ReentrantLock
  6. 一般同步、代碼簡潔優先?是 → synchronized
  7. 複雜異步編排?→ CompletableFuture / Reactor / RxJava
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved