Java ReentrantLock和synchronized的相同點與區別

1. 核心概念與定位

  • synchronized
    Java 內置的關鍵字,屬于 JVM 層面的隱式鎖。通過在方法或代碼塊上聲明,自動實現鎖的獲取與釋放,無需手動操作。設計目標是提供簡單易用的基礎同步能力,適合大多數常規同步場景。

  • ReentrantLock
    位于?java.util.concurrent.locks?包下的類,實現了?Lock?接口,屬于顯式鎖。需要通過?lock()?手動獲取鎖,unlock()?手動釋放鎖(通常在?finally?塊中執行)。設計目標是提供更靈活的同步控制,滿足復雜場景需求。

2. 核心共性

  • 可重入性:兩者都是可重入鎖,即同一線程可以多次獲取同一把鎖(如遞歸調用同步方法),不會產生死鎖。
  • 線程互斥:核心功能一致,都能保證同一時間只有一個線程進入臨界區,實現線程安全。

3. 關鍵區別

???(1)鎖的獲取與釋放方式
  • synchronized 是隱式鎖,自動獲取和釋放,無需手動操作
  • ReentrantLock 是顯式鎖,必須通過?lock()?獲取鎖,unlock()?釋放鎖
  • ReentrantLock 必須在 finally 塊中釋放鎖,否則可能因異常導致鎖無法釋放,造成死鎖
  • synchronized 更簡潔,ReentrantLock 更靈活但需更小心使用
// synchronized 示例
public class SynchronizedExample {private int count = 0;// 隱式獲取和釋放鎖public synchronized void increment() {count++; // 臨界區}
}// ReentrantLock 示例
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private int count = 0;private final ReentrantLock lock = new ReentrantLock();// 顯式獲取和釋放鎖public void increment() {lock.lock(); // 顯式獲取鎖try {count++; // 臨界區} finally {lock.unlock(); // 顯式釋放鎖(必須在finally中)}}
}
? ?(2)嘗試獲取鎖與超時機制

? ?ReentrantLock 支持超時獲取鎖

  • ReentrantLock 的?tryLock()?方法可以嘗試獲取鎖而不阻塞,或設置超時時間
  • 超時機制可以避免線程無限期等待鎖,提高系統的靈活性和穩定性

? ?synchronized 不支持超時機制

  • synchronized 一旦開始等待,就必須等到鎖釋放,無法主動放棄
// ReentrantLock 支持嘗試獲取鎖和超時
public class LockWithTimeout {private final ReentrantLock lock = new ReentrantLock();public boolean tryDoSomething() throws InterruptedException {// 嘗試獲取鎖,最多等待1秒if (lock.tryLock(1, TimeUnit.SECONDS)) {try {// 執行操作return true;} finally {lock.unlock();}}return false; // 獲取鎖失敗}
}// synchronized 不支持超時,必須一直等待
public class SynchronizedNoTimeout {public synchronized void doSomething() {// 無法設置超時,必須等待鎖釋放}
}
? (3)可中斷特性

???ReentrantLock 支持中斷

  • ReentrantLock 的?lockInterruptibly()?方法允許線程在等待鎖的過程中響應中斷
  • 可中斷特性在需要取消任務或關閉服務時非常有用

? ?synchronized 不支持中斷

  • synchronized 等待鎖的過程無法被中斷,可能導致線程一直阻塞
// ReentrantLock 支持中斷
public class InterruptibleLock {private final ReentrantLock lock = new ReentrantLock();public void doWithInterrupt() throws InterruptedException {// 可被中斷的鎖獲取lock.lockInterruptibly();try {// 執行操作} finally {lock.unlock();}}
}// synchronized 不支持中斷
public class SynchronizedNotInterruptible {public synchronized void doSomething() {// 即使線程被中斷,仍會繼續等待鎖}
}
(4)公平鎖實現

什么是 “公平鎖” 與 “非公平鎖”:

  • 公平鎖:線程獲取鎖的順序嚴格遵循 “請求鎖的先后順序”(FIFO),先請求的線程一定先拿到鎖,不會出現 “后請求的線程插隊” 的情況。
  • 非公平鎖:線程獲取鎖時不嚴格遵循請求順序,允許 “插隊”—— 即使隊列中有等待的線程,新到達的線程也可以嘗試直接搶占鎖,搶占失敗后再進入隊列排隊。

ReentrantLock 可實現公平鎖

  • ReentrantLock 可以通過構造函數參數?true?創建公平鎖
  • 公平鎖保證線程獲取鎖的順序與請求順序一致,避免線程饑餓
  • 非公平鎖允許線程 "插隊" 獲取鎖,可能導致某些線程長時間等待,但性能更高

synchronized 只能是非公平鎖

  • synchronized 始終是非公平鎖,無法改為公平鎖
// ReentrantLock 可創建公平鎖
public class FairLockExample {// 公平鎖:按請求順序獲取鎖private final ReentrantLock fairLock = new ReentrantLock(true);public void doWithFairLock() {fairLock.lock();try {// 執行操作} finally {fairLock.unlock();}}
}// synchronized 只能是非公平鎖
public class SynchronizedNonFair {// 無法設置為公平鎖,始終是非公平的public synchronized void doSomething() {// 執行操作}
}

為什么synchronized 非公平,不支持公平鎖?:

synchronized?的實現依賴 JVM 底層的?對象監視器(Monitor),其鎖分配邏輯本質是 “優先讓當前可執行的線程獲取鎖,減少線程切換開銷”,具體體現在兩個關鍵場景:

場景 1:新線程請求鎖時,直接 “插隊” 搶占

當一個線程 A 釋放鎖時,JVM 并不會立刻喚醒等待隊列中最前面的線程 B(公平鎖邏輯),而是會先檢查?當前是否有新線程 C 正在請求鎖
如果有新線程 C,JVM 會允許 C 直接搶占鎖(無需進入等待隊列),只有當沒有新線程時,才會喚醒隊列中的 B。

為什么這么做?
線程從 “等待狀態” 被喚醒,需要經歷?內核態→用戶態?的切換(操作系統級操作),這個過程開銷較大;而新線程 C 本身處于 “運行態”,直接讓它獲取鎖可以避免一次線程切換,顯著提升性能。

例如:

  • 線程 A 釋放鎖時,等待隊列中有線程 B(已等待 10ms);
  • 此時線程 C 剛執行到?synchronized?代碼塊,請求鎖;
  • JVM 會讓 C 直接拿到鎖,B 繼續等待;
  • 只有當 A 釋放鎖時沒有新線程,才喚醒 B。

場景 2:線程重入時,無需排隊

synchronized?是?可重入鎖(同一線程可多次獲取同一把鎖),而重入邏輯本身就是 “非公平” 的 —— 線程再次請求已持有的鎖時,無需進入等待隊列,直接成功獲取。

這是因為:線程持有鎖時,本身就有權限訪問臨界區,重入時跳過排隊是合理的,且能避免 “自己等自己” 的死鎖問題。但從公平性角度看,這相當于 “持有鎖的線程插隊”,優先于隊列中的其他線程。

例如:

  • 線程 A 已持有鎖,執行到一個嵌套的?synchronized?代碼塊;
  • 此時等待隊列中有線程 B;
  • 線程 A 無需排隊,直接重入鎖,B 繼續等待。

公平性的性能代價:

代價 1:強制線程切換,增加開銷

公平鎖要求嚴格按 “請求順序” 分配鎖,這意味著:

  • 當鎖釋放時,必須喚醒等待隊列中最前面的線程(不能讓新線程插隊);
  • 被喚醒的線程需要從 “等待態” 切換到 “運行態”,這個過程涉及操作系統內核操作,開銷遠大于 “新線程直接搶占”。

代價 2:鎖競爭激烈時,吞吐量下降

公平鎖會導致 “等待隊列越長,新線程越難獲取鎖”,即使新線程能快速執行完臨界區,也必須排隊。

例如:

  • 等待隊列中有 10 個線程,每個線程執行臨界區需要 100ms;
  • 此時有一個新線程,臨界區僅需 1ms;
  • 公平鎖下,新線程必須排在第 11 位,等待 10×100ms=1000ms 后才能執行,總耗時 1001ms;
  • 非公平鎖下,新線程可以直接搶占,總耗時 1ms(新線程)+ 10×100ms(隊列線程)= 1001ms?不 —— 實際是新線程執行 1ms 后釋放鎖,隊列中的第一個線程立刻執行,總耗時會更短(1ms + 100ms + ...),因為減少了一次線程切換的等待。

4.總結

ReentrantLock 和 synchronized 的核心區別可以概括為:

特性ReentrantLocksynchronized
鎖操作方式顯式(lock/unlock)隱式(自動獲取釋放)
靈活性高,支持多種獲取方式低,固定的獲取方式
超時獲取支持不支持
可中斷性支持不支持
公平性可選擇僅非公平
鎖狀態查詢可查詢(isLocked () 等)不可查詢
使用復雜度較高,需手動釋放低,不易出錯

synchronized 是 “簡單易用的基礎方案”,ReentrantLock 是 “靈活可控的高級方案”。現代 Java 版本中兩者性能差異不大,選擇時主要依據功能需求:簡單場景用 synchronized,復雜場景用 ReentrantLock。

適用場景:

  • 優先用 synchronized
    簡單同步場景(如普通方法 / 代碼塊同步)、追求代碼簡潔性、低并發場景。
    優勢:無需手動釋放鎖,減少出錯概率,JVM 持續優化(如偏向鎖、輕量級鎖)。

  • 優先用 ReentrantLock
    需要超時獲取鎖、可中斷鎖、公平鎖的場景;需要多個條件變量精確控制線程喚醒;復雜同步邏輯(如生產者 - 消費者模型的精細化控制)。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/98816.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/98816.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/98816.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【npm】npm 包更新工具 npm-check-updates (ncu)

npm 包太多了,一個項目有那么多依賴包,它們的升級管理需要一個工具:npm-check-updates: 安裝: npm install -g npm-check-updates安裝之后,就可以使用它的命令:ncu 查看哪些包可以升級&#xff…

go資深之路筆記(一) Context

一、 Context 的正確使用與底層原理 1.結構體 type Context interface {// Deadline 返回此 Context 被取消的時間點。// 如果未設置截止時間,ok 為 false。Deadline() (deadline time.Time, ok bool)// Done 返回一個 channel。當 Context 被取消或超時后&#xff…

VS2022 + Qt5.9 中文亂碼/項目設置utf-8編碼

🛠? 解決QT5.9 VS2022中文亂碼的全面方案 📁 1. 檢查文件編碼與編譯器設置 確保源文件是 帶BOM的UTF-8 編碼對MSVC編譯器很重要。VS2022默認可能使用本地編碼(如GB2312)解析源文件,即使文件以UTF-8保存。 查看和設置…

數據庫--MySQL數據管理

數據庫–MySQL數據管理 文章目錄數據庫--MySQL數據管理1.外鍵管理2.數據庫數據管理3.DML語言3.1添加數據3.2修改數據3.3刪除數據4.練習1.外鍵管理 外鍵概念 如果公共關鍵字在一個關系中是主關鍵字,那么這個公共關鍵字被稱為另一個關系的外鍵。由此可見,…

【C++練習】13.C++輸出九九乘法表的方法詳解

目錄 C++輸出九九乘法表的方法詳解 方法1:雙重for循環(最基礎) 思考: 代碼分析: 特點: 方法2:使用while循環 思考: 代碼分析: 特點: 方法3:使用遞歸實現 思考: 代碼分析: 特點: 方法4:格式化輸出(對齊美觀) 思考: 代碼分析: 特點: 方法5:使用函數封裝 思考…

MVC及其衍生

MVC 把軟件分成模型(Model)、視圖(View)、控制器(Controller)三個基本部分。 事實上對應著 Controller——輸入 用戶交互,將輸入處理成Controller能處理的形式 Model——處理 描述狀態、邏輯規律…

微碩WINSOK MOS管WSF3089,賦能汽車轉向系統安全升級

隨著汽車電子化程度不斷提高,轉向系統對高效功率器件的需求日益增長。微碩WINSOK推出的N溝道Trench MOS管WSF3089,以30 V/72 A大電流、4.5 mΩ超低導通電阻和TO-252-2L緊湊封裝,為EPS(電動助力轉向)電機驅動、電源管理…

淘寶拍立淘接口的接入與應用||item_search_img-按圖搜索淘寶商品(拍立淘)

淘寶拍立淘接口的接入與應用如下:接入流程注冊與認證:開發者賬號注冊:訪問淘寶開放平臺,進行開發者賬號注冊。創建應用:在控制臺創建新應用,獲取 App Key 和 App Secret,這是接口調用的憑證。申…

Python學習-day8 元組tuple

元組(Tuple)是Python中一種不可變的序列類型,用于存儲多個有序元素。與列表(List)類似,但元組一旦創建后不能修改(不可添加、刪除或修改元素),這使得它在安全性、性能優化…

大數據畢業設計選題推薦-基于大數據的國家醫用消耗選品采集數據可視化分析系統-Hadoop-Spark-數據可視化-BigData

?作者主頁:IT畢設夢工廠? 個人簡介:曾從事計算機專業培訓教學,擅長Java、Python、PHP、.NET、Node.js、GO、微信小程序、安卓Android等項目實戰。接項目定制開發、代碼講解、答辯教學、文檔編寫、降重等。 ?文末獲取源碼? 精彩專欄推薦?…

二次學習C語言補充2

文章目錄表棧、隊列、二叉樹一、二叉樹二、表棧三、隊列鏈表一、單向鏈表二、循環鏈表、雙向鏈表和雙向循環鏈表預處理一、預處理二、宏定義文件文件操作補充本篇文章是對二次學習C語言12——文件操作 二次學習C語言14——預處理及模塊化 二次學習C語言15——鏈表 二次學習C語言…

2.9Vue創建項目(組件)的補充

1.再創建和引入vue的選擇2.VsCode插件 安裝Vue自己搜索最新的3.style自己的作用域在一個組件中引入另一個文件的子組件,給當前組件設置樣式,那么子組件的樣式也會改變的。為了解決這個問題 我們在自己的style中設置一個屬性4.另一種創建vue 的方式(主流…

算法高頻題

刷題:LeetCode(Top 100-150題,至少刷兩遍)。重點:鏈表、樹、二分查找、動態規劃、回溯、棧/隊列。 每一個題型,前10個高頻題 算法思考框架參考:算法題思維框架-CSDN博客 高頻順序參考網站&…

服務器安裝 LDOPE(MODIS 數據處理工具)

目錄下載方式1-(簡單快捷)根據WRF-VPRM 需要打補丁下載方式2:(手動安裝依賴)一、安裝所需依賴庫(4 個主庫 2 個基礎庫)另- HDF-EOS 手動編譯二、解壓并安裝 LDOPE參考下載方式1-(簡…

克隆代幣 + 捆綁開盤:多鏈環境下的低成本發幣玩法

在加密世界,發幣已經不再是“少數開發者的專利”。隨著工具的普及,任何人都可以快速發行一個在加密世界,發幣已經不再是“少數開發者的專利”。隨著工具的普及,任何人都可以快速發行一個代幣。但問題是:如何在保證低成…

數據結構中的 二叉樹

1.前言 在 Java 中,樹(Tree)是一種非線性數據結構,由節點(Node)組成,常見的線性表則是我們之前學過的順序表、鏈表、棧、隊列等等。每個節點包含數據和指向子節點的引用。樹的常見實現方式包括二…

IntelliJ IDEA 啟動項目時配置端口指南

🌟 一、為什么需要手動設置啟動端口? 默認情況下,Spring Boot 應用會使用 8080 端口啟動。但在以下場景中,我們必須自定義端口: 多個微服務同時運行,需避免端口沖突;團隊協作開發,統…

spark sql之from_json函數

目錄前言函數語法參數說明返回值案例案例1案例2前言 在Spark SQL中,from_json函數用于解析包含JSON字符串的列,并將其轉換為Spark SQL的結構化類型(如struct、map或array) 函數語法 from_json(jsonStr, schema [, options])參數…

數據結構 之 【位圖的簡介】

目錄 1.位圖的引入 2.位圖概念 3.位圖的實現 3.1前提準備 3.2set 3.3reset 3.4test 4.位圖的應用 1.位圖的引入 給40億個不重復的無符號整數,沒排過序 再給一個無符號整數,如何快速判斷這個無符號整數是否在 這40億個數中 首先,一個…

[iOS] ViewController 的生命周期

文章目錄前言一、UIViewController 生命周期有關函數二、UIViewController 中函數的執行順序運行結果1.present和dismiss2.push和pop三、總結前言 UIViewController 是在 iOS 開發中一個非常重要的角色,他是 view 和 model 的橋梁,通過 UIViewControlle…