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

深度解析 Java Lock 機制:ReentrantLock vs synchronized,可中斷鎖實戰

     編輯:Java 綜合教學

深度解析 Java Lock 機制:ReentrantLock vs synchronized,可中斷鎖實戰

JDK 5 引入 java.util.concurrent.locks.Lock 接口,為並發編程提供了比內置鎖 synchronized 更豐富、更靈活的同步機制。本文從源碼、實現策略、選型建議、可中斷鎖實戰四個維度深度剖析。

一、Lock 接口核心方法速覽

public interface Lock {
    void lock();                    // 無條件獲取鎖
    void lockInterruptibly() throws InterruptedException; // 可中斷獲取鎖
    boolean tryLock();              // 非阻塞嘗試獲取鎖
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // 定時嘗試
    void unlock();                  // 釋放鎖
    Condition newCondition();       // 獲取條件變量(支持多條件等待)
}

二、ReentrantLock vs synchronized:並發策略的本質差異

1. synchronized:悲觀並發策略(阻塞同步)

  • 採用互斥鎖,線程爭用失敗進入 BLOCKED 狀態,需 OS 內核介入(用戶態↔內核態切換)。
  • 優點:JVM 原生優化(偏向鎖、輕量級鎖、鎖消除、鎖粗化、自適應自旋),語法簡單,不易出錯。
  • 缺點:無法中斷等待、無法超時獲取、不支持公平鎖、只能綁定單一條件變量。

2. ReentrantLock:樂觀並發策略(CAS + 自旋)

  • 基於 AQS (AbstractQueuedSynchronizer),爭用失敗時在用戶態自旋(CAS 重試),不進入內核,實現非阻塞同步。
  • 核心優勢:
    • 可中斷鎖:lockInterruptibly() 允許等待線程響應中斷,提升系統靈活性。
    • 公平鎖選項:構造函數 new ReentrantLock(true) 保證 FIFO 獲取鎖(吞吐率較低)。
    • 多條件變量:newCondition() 可創建多個 Condition,精準喚醒(如「不滿」喚醒生產者、「不空」喚醒消費者)。
    • 非塊結構鎖:可在不同方法、不同代碼塊中 lock/unlock(需配合 try-finally)。

三、為何大師仍建議優先使用 synchronized?

「除非需要 Lock 的高級特性,否則優先用 synchronized。」—— Brian Goetz

  1. 安全性:synchronized 隱式釋放鎖,杜絕忘記 unlock() 導致死鎖風險。
  2. 可診斷性:JVM 線程 dump 可精準識別 synchronized 鎖持有者、等待隊列、死鎖鏈;Lock 鎖在 dump 中不可見。
  3. 持續優化:synchronized 是 JVM 內建屬性,未來優化空間大(如偏向鎖、輕量級鎖);Lock 基於 AQS,優化上限受限。

四、最佳實踐模板

// 標準 Lock 使用模式
Lock lock = new ReentrantLock(); // 或 new ReentrantLock(true) 公平鎖
lock.lock();
try {
    // 臨界區操作
    // 若有 return 必須放在 try 內
} finally {
    lock.unlock(); // 必須在 finally 中釋放
}

// 可中斷鎖模式
lock.lockInterruptibly();
try {
    // 耗時操作,可被中斷
} catch (InterruptedException e) {
    // 處理中斷,恢復中斷狀態
    Thread.currentThread().interrupt();
} finally {
    if (lock.isHeldByCurrentThread()) lock.unlock();
}

五、實戰:可中斷鎖 vs synchronized 中斷響應對比

場景:寫線程持有鎖 15 秒,讀線程等待 5 秒後被主線程中斷。

synchronized 版本:中斷無效,必須等待寫線程釋放鎖

// 讀線程
synchronized (lock) {
    System.out.println("start reading...");
}
// 主線程 sleep 5s 後 reader.interrupt()
// 結果:讀線程在 15 秒後才打印 "finish reading",中斷異常被吞噬/延遲

ReentrantLock 版本:讀線程立即響應中斷拋出 InterruptedException

// 讀線程
lock.lockInterruptibly();
try {
    System.out.println("start reading...");
} finally {
    lock.unlock();
}
// 主線程 sleep 5s 後 reader.interrupt()
// 結果:讀線程 5 秒處拋出 InterruptedException,打印 "interrupt reading",不再等待寫線程

synchronized 中斷無效運行結果 ReentrantLock 中斷生效運行結果

六、選型決策表

場景推薦理由
簡單同步、臨界區短synchronized代碼簡潔、JVM 優化充分、無死鎖風險
需響應中斷(取消任務、超時控制)ReentrantLock.lockInterruptibly()唯一標準 API 實現可中斷獲取鎖
需公平調度(避免線程饑餓)new ReentrantLock(true)synchronized 非公平,Lock 可選公平
複雜等待/通知邏輯(多條件)Lock + 多個 Conditionsynchronized 僅支持單一 wait/notify
跨方法/跨塊加鎖Lock非塊結構靈活性

七、總結

Lock 機制通過 AQS 實現了用戶態非阻塞同步,賦予了可中斷、公平、多條件等高級能力,是構建高性能並發組件(如 ReentrantReadWriteLock、Semaphore、CountDownLatch)的基石。但日常業務開發中,synchronized 因其簡單性、安全性、可診斷性及 JVM 持續優化,仍是首選。掌握兩者差異,按需選型,方為並發編程正道。

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