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

Java 鎖機制全景圖:從 synchronized 到 Lock、ReadWriteLock、StampedLock 核心原理與選型

     編輯:Java 綜合教學

Java 鎖機制全景圖:從 synchronized 到 Lock、ReadWriteLock、StampedLock 核心原理與選型

鎖是並發編程的基石。Java 提供了從內置鎖 synchronized 到顯式鎖 Lock 體系的完整工具箱。本文系統梳理鎖的分類、核心實現原理、關鍵特性對比,並給出生產環境選型指南。

一、鎖的兩大體系

維度內置鎖 synchronized顯式鎖 Lock (JUC)
實現層級JVM 原生 (monitorenter/monitorexit)Java 代碼 (AQS + CAS)
釋放方式自動 (塊結束/異常)手動 unlock() (需 finally)
可中斷不可中斷lockInterruptibly() 支持
超時獲取不支持tryLock(timeout) 支持
公平性非公平可選公平/非公平
條件變量wait/notify (單條件)newCondition() (多條件)
性能 (低爭用)優化後極佳 (偏向鎖/輕量級鎖)略高開銷
性能 (高爭用)較差優秀 (CAS + 自旋 + 鎖升級)

二、synchronized 深度解析

1. 三種鎖定範圍

public class SyncDemo {
    private static final Object LOCK = new Object();
    
    // 1. 實例方法鎖:鎖 this
    public synchronized void instanceMethod() { ... }
    
    // 2. 靜態方法鎖:鎖 Class 對象
    public synchronized static void staticMethod() { ... }
    
    // 3. 代碼塊鎖:鎖任意對象(粒度最細)
    public void blockSync() {
        synchronized (LOCK) { ... } // 推薦:私有 final Object
        // synchronized (this) { ... } // 等同實例方法鎖
    }
}

2. 可重入性

同一線程可重複獲取同一鎖。JVM 維護 計數器 + 持有線程:

  • 進入同步塊:計數器 +1
  • 退出同步塊:計數器 -1
  • 計數器 = 0:釋放鎖

這使得遞歸調用、父子類同步方法調用不會死鎖。

3. 鎖升級機制 (JDK 1.6+)

無鎖 → 偏向鎖 → 輕量級鎖 (CAS 自旋) → 重量級鎖 (OS Mutex)。
-XX:-UseBiasedLocking 可禁用偏向鎖。

三、Lock 體系核心實現

1. ReentrantLock:可重入獨佔鎖

private final Lock lock = new ReentrantLock(); // 非公平(默認)
// private final Lock lock = new ReentrantLock(true); // 公平

public void doWork() {
    lock.lock(); // 必須在 try 外獲取,防止拋異常未釋放
    try {
        // 臨界區
    } finally {
        lock.unlock(); // 必須在 finally 中釋放
    }
}

2. 進階特性

  • tryLock():非阻塞嘗試獲取,立即返回 true/false
  • tryLock(timeout, unit):限時等待,避免無限阻塞
  • lockInterruptibly():可響應中斷的獲取,拋出 InterruptedException

3. 條件變量 Condition

private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();

// 生產者
lock.lock();
try {
    while (queue.size() == capacity) notFull.await();
    queue.add(item);
    notEmpty.signal();
} finally { lock.unlock(); }

// 消費者
lock.lock();
try {
    while (queue.isEmpty()) notEmpty.await();
    Item item = queue.remove();
    notFull.signal();
    return item;
} finally { lock.unlock(); }

一把鎖可關聯多個 Condition,實現精確通知(signalAll 喚醒所有)。

四、ReadWriteLock:讀寫分離鎖

適用「讀多寫少」場景:讀讀並發、讀寫互斥、寫寫互斥。

1. ReentrantReadWriteLock

private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();

// 讀操作
readLock.lock();
try { return data; } finally { readLock.unlock(); }

// 寫操作
writeLock.lock();
try { data = newValue; } finally { writeLock.unlock(); }

2. 核心機制

  • 狀態變量 state 高 16 位存讀鎖計數,低 16 位存寫鎖計數
  • 支持鎖降級:持有寫鎖 → 獲取讀鎖 → 釋放寫鎖(保證數據可見性)
  • 不支持鎖升級:持有讀鎖時嘗試獲取寫鎖會死鎖
  • 公平模式:FIFO 隊列,寫鎖飢餓風險低;非公平:吞吐高

五、StampedLock:樂觀讀鎖 (JDK 8+)

進一步優化讀多寫少:無鎖樂觀讀 + 版本戳驗證。

private final StampedLock sl = new StampedLock();

// 樂觀讀(極快,無鎖)
public double readOptimistic() {
    long stamp = sl.tryOptimisticRead();
    double x = this.x, y = this.y; // 讀取共享變量
    if (!sl.validate(stamp)) { // 驗證期間無寫入
        // 降級為悲觀讀
        stamp = sl.readLock();
        try {
            x = this.x; y = this.y;
        } finally { sl.unlockRead(stamp); }
    }
    return Math.sqrt(x*x + y*y);
}

// 悲觀讀
public double readPessimistic() {
    long stamp = sl.readLock();
    try { return Math.sqrt(x*x + y*y); }
    finally { sl.unlockRead(stamp); }
}

// 寫操作
public void write(double x, double y) {
    long stamp = sl.writeLock();
    try { this.x = x; this.y = y; }
    finally { sl.unlockWrite(stamp); }
}

注意:StampedLock 不可重入,不支持 Condition,慎用於複雜邏輯。

六、其他同步工具

工具核心用途關鍵方法
CountDownLatch一個或多個線程等待 N 個事件完成countDown(), await()
CyclicBarrierN 個線程相互等待,達到屏障點同時繼續await()
Semaphore控制同時訪問資源的線程數 (許可證)acquire(), release()
Exchanger兩線程交換數據exchange(V)

七、選型決策樹

  1. 簡單同步、低爭用 → synchronized (JVM 優化最好,代碼最簡潔)
  2. 需要可中斷/超時/多條件/公平鎖 → ReentrantLock
  3. 明確讀多寫少、數據結構不可變/可複製 → ReentrantReadWriteLock
  4. 極度讀多寫少、可容忍偶爾重試、追求極致讀性能 → StampedLock
  5. 線程協作等待 → CountDownLatch/CyclicBarrier
  6. 資源池限流 → Semaphore

八、避坑指南

  • Lock 必須 lock() 在 try 外,unlock() 在 finally 中
  • 避免在鎖內調用異步/耗時/IO/其他鎖操作
  • 嵌套鎖嚴格遵循固定順序,防死鎖
  • ReentrantReadWriteLock 寫鎖降級讀鎖模式要熟練掌握
  • StampedLock 不可重入,同一線程重複獲取會卡死
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved