Java中synchronized和ReentrantLock的區(qū)別對比
一、基本特性對比
| 特性 | synchronized | ReentrantLock |
| 鎖的實現(xiàn)機制 | JVM 內(nèi)置關鍵字,通過監(jiān)視器實現(xiàn) | JDK 提供的 API 類(java.util.concurrent.locks) |
| 鎖的獲取方式 | 隱式獲取和釋放(進入/退出同步代碼塊或方法自動獲取/釋放) | 顯式調(diào)用 lock()/unlock()方法 |
| 可重入性 | 支持 | 支持 |
| 鎖的類型 | 非公平鎖(默認) | 可選擇公平鎖或非公平鎖(構造函數(shù)指定) |
| 條件變量 | 通過 wait()/notify()/notifyAll()實現(xiàn) | 通過 Condition對象支持多個條件隊列 |
| 中斷響應 | 不支持中斷等待 | 支持 lockInterruptibly()中斷等待 |
| 超時機制 | 不支持 | 支持 tryLock(timeout, unit)嘗試獲取鎖 |
| 鎖的綁定 | 與代碼塊或方法綁定 | 可跨方法綁定,更靈活 |
| 性能 | JDK 1.6 后優(yōu)化,性能接近 | 在高并發(fā)競爭下表現(xiàn)更穩(wěn)定 |
二、詳細區(qū)別分析
1. 實現(xiàn)層面
synchronized:
- Java 關鍵字,由 JVM 底層實現(xiàn)(通過 monitorenter/monitorexit 字節(jié)碼指令)。
- 鎖信息記錄在對象頭的 Mark Word 中。
ReentrantLock:
- 基于 AbstractQueuedSynchronizer(AQS) 實現(xiàn)的顯式鎖。
- 通過 CAS(Compare-And-Swap)和隊列管理線程競爭。
2. 使用方式
// synchronized 隱式使用
public synchronized void method() {
// 同步代碼
}
// 或
public void method() {
synchronized(this) {
// 同步代碼
}
}
// ReentrantLock 顯式使用
private ReentrantLock lock = new ReentrantLock();
public void method() {
lock.lock();
try {
// 同步代碼
} finally {
lock.unlock(); // 必須手動釋放
}
}3. 公平性選擇
synchronized:僅支持非公平鎖(線程競爭時隨機獲取鎖)。
ReentrantLock:
- 公平鎖:按等待時間順序獲取鎖,避免線程饑餓,但性能較低。
- 非公平鎖:允許插隊,性能更高。
4. 條件變量(Condition)
- synchronized:通過 Object.wait()/notify() 實現(xiàn)等待/喚醒,只能有一個等待隊列。
- ReentrantLock:可創(chuàng)建多個 Condition 對象,實現(xiàn)精細化的線程等待/喚醒。
Condition condition = lock.newCondition(); condition.await(); // 類似 wait() condition.signal(); // 類似 notify()
示例:生產(chǎn)者-消費者模型中,可為空隊列和滿隊列分別設置 Condition。
5. 中斷與超時
synchronized:
- 線程等待鎖時無法被中斷。
- 無超時機制,可能永久等待。
ReentrantLock:
// 支持中斷
lock.lockInterruptibly();
// 支持超時
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try { /* 操作 */ }
finally { lock.unlock(); }
}6. 性能差異
- JDK 1.5 時 ReentrantLock 性能顯著優(yōu)于 synchronized。
- JDK 1.6 后 JVM 對 synchronized 進行了大量優(yōu)化(鎖升級、自適應自旋等),兩者性能差距縮小。
- 在高競爭場景下,ReentrantLock 仍可能表現(xiàn)更穩(wěn)定。
三、適用場景
優(yōu)先使用 synchronized 的情況
- 簡單的同步場景,代碼簡潔性更重要。
- 不需要高級功能(如條件變量、中斷、超時)。
- 資源競爭不激烈時,性能可接受。
優(yōu)先使用 ReentrantLock 的情況
- 需要公平鎖、可中斷鎖、超時鎖等高級功能。
- 需要多個條件變量(如阻塞隊列的實現(xiàn))。
- 需要跨方法加鎖/釋放鎖(如:在方法 A 加鎖,在方法 B 釋放)。
- 競爭激烈且性能要求高。
四、示例對比
場景:生產(chǎn)者-消費者模型
// 使用 synchronized(單一條件)
public synchronized void put(Object item) throws InterruptedException {
while (queue.isFull()) {
wait(); // 只能在一個條件上等待
}
queue.put(item);
notifyAll();
}
// 使用 ReentrantLock(多條件)
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
public void put(Object item) throws InterruptedException {
lock.lock();
try {
while (queue.isFull()) {
notFull.await(); // 只在 "非滿" 條件上等待
}
queue.put(item);
notEmpty.signal(); // 只喚醒等待 "非空" 的線程
} finally {
lock.unlock();
}
}五、總結
- synchronized 簡單、安全、自動管理鎖釋放,適合大多數(shù)常規(guī)同步場景。
- ReentrantLock 功能強大、靈活可控,適合復雜并發(fā)場景和高級需求。
- 從 JDK 1.6 開始,兩者性能接近,選擇時應更關注功能需求和代碼可維護性。
- 在 JDK 后續(xù)版本中,synchronized 仍在持續(xù)優(yōu)化(如鎖消除、鎖粗化等),而 ReentrantLock 提供了更細粒度的并發(fā)控制。
面試回答
首先,synchronized 是 Java 語言層面的關鍵字,是 JVM 原生支持的鎖機制。它的使用非常簡單,編譯器會自動處理鎖的獲取和釋放,所以基本不會因為忘記釋放鎖而導致死鎖,易用性是它的最大優(yōu)點。
而 ReentrantLock 是 JUC 包下的一個類,是 JDK 層面實現(xiàn)的鎖。它需要開發(fā)者顯式地調(diào)用 lock() 和 unlock() 方法,通常在 finally 塊中釋放鎖,否則容易出問題。所以從使用門檻上說,synchronized 更低。
在功能上,ReentrantLock 比 synchronized 靈活和強大得多,主要有三點:
- 可中斷獲取鎖:當線程嘗試獲取 ReentrantLock 時,如果長時間拿不到,可以響應中斷,通過 lockInterruptibly() 方法放棄等待去做別的事情。而 synchronized 在等待鎖時,線程會一直阻塞,無法被中斷。
- 公平鎖選項:ReentrantLock 可以在構造函數(shù)中指定是否是公平鎖(先等待的線程先獲得鎖)。雖然公平鎖性能有損耗,但能防止線程饑餓。synchronized 則是非公平的,誰搶到算誰的,性能通常更好。
- 條件變量(Condition):這是非常強大的一點。一個 ReentrantLock 可以創(chuàng)建多個 Condition 對象,用來實現(xiàn)更精細的線程等待/通知。比如,我們可以讓一部分線程在條件A上等待,另一部分在條件B上等待,喚醒時也可以選擇只喚醒等待條件A的線程。而 synchronized 只能配合 wait() 和 notify(),所有線程都在同一個條件隊列上,喚醒是隨機的(notify)或全部喚醒(notifyAll),不夠精確。
在早期版本(JDK 1.5 之前),ReentrantLock 的性能比 synchronized 好很多。但后來 JVM 對 synchronized 進行了大幅優(yōu)化,比如引入了偏向鎖、輕量級鎖、自旋鎖、鎖消除、鎖粗化等。所以在高版本的 JDK(如 1.8 及以后)中,兩者在性能上已經(jīng)相差無幾,synchronized 甚至在一些常見場景下更優(yōu),因為它有 JVM 的持續(xù)優(yōu)化。
所以,我的選擇原則通常是:
- 優(yōu)先考慮 synchronized:在滿足需求的情況下,因為它簡單、安全(自動釋放),且性能不差。大部分標準的同步場景用它就夠了。
- 需要高級功能時再用 ReentrantLock:比如我需要用到可中斷、公平鎖,或者需要復雜的條件等待機制(典型應用就是“生產(chǎn)者-消費者”模型),這時 ReentrantLock 是唯一的選擇。
以上就是Java中synchronized和ReentrantLock的區(qū)別對比的詳細內(nèi)容,更多關于Java synchronized與ReentrantLock的資料請關注腳本之家其它相關文章!
相關文章
Spring Scheduler定時任務實戰(zhàn)指南(零基礎入門任務調(diào)度)
本文介紹SpringScheduler在電商訂單超時處理中的應用,涵蓋啟用定時任務、使用@Scheduled注解、cron表達式配置、線程池優(yōu)化及異步執(zhí)行等核心內(nèi)容,本文給大家介紹Spring Scheduler定時任務實戰(zhàn)指南,感興趣的朋友跟隨小編一起看看吧2025-09-09
IntelliJ IDEA設置代碼的快捷編輯模板Live Templates
今天小編就為大家分享一篇關于IntelliJ IDEA設置代碼的快捷編輯模板Live Templates,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-10-10
SpringMVC框架中使用Filter實現(xiàn)請求日志打印方式
這篇文章主要介紹了SpringMVC框架中使用Filter實現(xiàn)請求日志打印方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10
Spring Boot產(chǎn)生環(huán)形注入的解決方案
這篇文章主要介紹了Spring Boot產(chǎn)生環(huán)形注入的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09

