synchronized?vs?ReentrantLock性能對(duì)比:哪個(gè)更適合高并發(fā)場(chǎng)景?
一、基本特性對(duì)比
特性 | synchronized | ReentrantLock |
鎖的實(shí)現(xiàn)機(jī)制 | JVM 內(nèi)置關(guān)鍵字,通過(guò)監(jiān)視器實(shí)現(xiàn) | JDK 提供的 API 類(lèi)( |
鎖的獲取方式 | 隱式獲取和釋放(進(jìn)入/退出同步代碼塊或方法自動(dòng)獲取/釋放) | 顯式調(diào)用 |
可重入性 | 支持 | 支持 |
鎖的類(lèi)型 | 非公平鎖(默認(rèn)) | 可選擇公平鎖或非公平鎖(構(gòu)造函數(shù)指定) |
條件變量 | 通過(guò) | 通過(guò) |
中斷響應(yīng) | 不支持中斷等待 | 支持 |
超時(shí)機(jī)制 | 不支持 | 支持 |
鎖的綁定 | 與代碼塊或方法綁定 | 可跨方法綁定,更靈活 |
性能 | JDK 1.6 后優(yōu)化,性能接近 | 在高并發(fā)競(jìng)爭(zhēng)下表現(xiàn)更穩(wěn)定 |
二、詳細(xì)區(qū)別分析
1. 實(shí)現(xiàn)層面
- synchronized:
- Java 關(guān)鍵字,由 JVM 底層實(shí)現(xiàn)(通過(guò)
monitorenter/monitorexit字節(jié)碼指令)。 - 鎖信息記錄在對(duì)象頭的 Mark Word 中。
- Java 關(guān)鍵字,由 JVM 底層實(shí)現(xiàn)(通過(guò)
- ReentrantLock:
- 基于
AbstractQueuedSynchronizer(AQS)實(shí)現(xiàn)的顯式鎖。 - 通過(guò) CAS(Compare-And-Swap)和隊(duì)列管理線程競(jìng)爭(zhēng)。
- 基于
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(); // 必須手動(dòng)釋放
}
}3. 公平性選擇
- synchronized:僅支持非公平鎖(線程競(jìng)爭(zhēng)時(shí)隨機(jī)獲取鎖)。
- ReentrantLock:
- 公平鎖:按等待時(shí)間順序獲取鎖,避免線程饑餓,但性能較低。
- 非公平鎖:允許插隊(duì),性能更高。
4. 條件變量(Condition)
- synchronized:通過(guò)
Object.wait()/notify()實(shí)現(xiàn)等待/喚醒,只能有一個(gè)等待隊(duì)列。 - ReentrantLock:可創(chuàng)建多個(gè)
Condition對(duì)象,實(shí)現(xiàn)精細(xì)化的線程等待/喚醒。
Condition condition = lock.newCondition(); condition.await(); // 類(lèi)似 wait() condition.signal(); // 類(lèi)似 notify()
示例:生產(chǎn)者-消費(fèi)者模型中,可為空隊(duì)列和滿隊(duì)列分別設(shè)置 Condition。
5. 中斷與超時(shí)
- synchronized:
- 線程等待鎖時(shí)無(wú)法被中斷。
- 無(wú)超時(shí)機(jī)制,可能永久等待。
- ReentrantLock:
// 支持中斷
lock.lockInterruptibly();
// 支持超時(shí)
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try { /* 操作 */ }
finally { lock.unlock(); }
}6. 性能差異
- JDK 1.5 時(shí)
ReentrantLock性能顯著優(yōu)于synchronized。 - JDK 1.6 后 JVM 對(duì)
synchronized進(jìn)行了大量?jī)?yōu)化(鎖升級(jí)、自適應(yīng)自旋等),兩者性能差距縮小。 - 在高競(jìng)爭(zhēng)場(chǎng)景下,
ReentrantLock仍可能表現(xiàn)更穩(wěn)定。
三、適用場(chǎng)景
優(yōu)先使用 synchronized 的情況
- 簡(jiǎn)單的同步場(chǎng)景,代碼簡(jiǎn)潔性更重要。
- 不需要高級(jí)功能(如條件變量、中斷、超時(shí))。
- 資源競(jìng)爭(zhēng)不激烈時(shí),性能可接受。
優(yōu)先使用 ReentrantLock 的情況
- 需要公平鎖、可中斷鎖、超時(shí)鎖等高級(jí)功能。
- 需要多個(gè)條件變量(如阻塞隊(duì)列的實(shí)現(xiàn))。
- 需要跨方法加鎖/釋放鎖(如:在方法 A 加鎖,在方法 B 釋放)。
- 競(jìng)爭(zhēng)激烈且性能要求高。
四、示例對(duì)比
場(chǎng)景:生產(chǎn)者-消費(fèi)者模型
// 使用 synchronized(單一條件)
public synchronized void put(Object item) throws InterruptedException {
while (queue.isFull()) {
wait(); // 只能在一個(gè)條件上等待
}
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();
}
}五、總結(jié)
- synchronized 簡(jiǎn)單、安全、自動(dòng)管理鎖釋放,適合大多數(shù)常規(guī)同步場(chǎng)景。
- ReentrantLock 功能強(qiáng)大、靈活可控,適合復(fù)雜并發(fā)場(chǎng)景和高級(jí)需求。
- 從 JDK 1.6 開(kāi)始,兩者性能接近,選擇時(shí)應(yīng)更關(guān)注功能需求和代碼可維護(hù)性。
- 在 JDK 后續(xù)版本中,
synchronized仍在持續(xù)優(yōu)化(如鎖消除、鎖粗化等),而ReentrantLock提供了更細(xì)粒度的并發(fā)控制。
面試回答
首先,synchronized 是 Java 語(yǔ)言層面的關(guān)鍵字,是 JVM 原生支持的鎖機(jī)制。它的使用非常簡(jiǎn)單,編譯器會(huì)自動(dòng)處理鎖的獲取和釋放,所以基本不會(huì)因?yàn)橥涐尫沛i而導(dǎo)致死鎖,易用性是它的最大優(yōu)點(diǎn)。
而 ReentrantLock 是 JUC 包下的一個(gè)類(lèi),是 JDK 層面實(shí)現(xiàn)的鎖。它需要開(kāi)發(fā)者顯式地調(diào)用 lock() 和 unlock() 方法,通常在 finally 塊中釋放鎖,否則容易出問(wèn)題。所以從使用門(mén)檻上說(shuō),synchronized 更低。
在功能上,ReentrantLock 比 synchronized 靈活和強(qiáng)大得多,主要有三點(diǎn):
- 可中斷獲取鎖:當(dāng)線程嘗試獲取
ReentrantLock時(shí),如果長(zhǎng)時(shí)間拿不到,可以響應(yīng)中斷,通過(guò)lockInterruptibly()方法放棄等待去做別的事情。而synchronized在等待鎖時(shí),線程會(huì)一直阻塞,無(wú)法被中斷。 - 公平鎖選項(xiàng):
ReentrantLock可以在構(gòu)造函數(shù)中指定是否是公平鎖(先等待的線程先獲得鎖)。雖然公平鎖性能有損耗,但能防止線程饑餓。synchronized則是非公平的,誰(shuí)搶到算誰(shuí)的,性能通常更好。 - 條件變量(Condition):這是非常強(qiáng)大的一點(diǎn)。一個(gè)
ReentrantLock可以創(chuàng)建多個(gè)Condition對(duì)象,用來(lái)實(shí)現(xiàn)更精細(xì)的線程等待/通知。比如,我們可以讓一部分線程在條件A上等待,另一部分在條件B上等待,喚醒時(shí)也可以選擇只喚醒等待條件A的線程。而synchronized只能配合wait()和notify(),所有線程都在同一個(gè)條件隊(duì)列上,喚醒是隨機(jī)的(notify)或全部喚醒(notifyAll),不夠精確。
在早期版本(JDK 1.5 之前),ReentrantLock 的性能比 synchronized 好很多。但后來(lái) JVM 對(duì) synchronized 進(jìn)行了大幅優(yōu)化,比如引入了偏向鎖、輕量級(jí)鎖、自旋鎖、鎖消除、鎖粗化等。所以在高版本的 JDK(如 1.8 及以后)中,兩者在性能上已經(jīng)相差無(wú)幾,synchronized 甚至在一些常見(jiàn)場(chǎng)景下更優(yōu),因?yàn)樗?JVM 的持續(xù)優(yōu)化。
所以,我的選擇原則通常是:
- 優(yōu)先考慮
synchronized:在滿足需求的情況下,因?yàn)樗?jiǎn)單、安全(自動(dòng)釋放),且性能不差。大部分標(biāo)準(zhǔn)的同步場(chǎng)景用它就夠了。 - 需要高級(jí)功能時(shí)再用
ReentrantLock:比如我需要用到可中斷、公平鎖,或者需要復(fù)雜的條件等待機(jī)制(典型應(yīng)用就是“生產(chǎn)者-消費(fèi)者”模型),這時(shí)ReentrantLock是唯一的選擇。
到此這篇關(guān)于synchronized vs ReentrantLock性能對(duì)比:哪個(gè)更適合高并發(fā)場(chǎng)景?的文章就介紹到這了,更多相關(guān)synchronized和ReentrantLock的區(qū)別內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java鎖機(jī)制synchronized與ReentrantLock全方面解析
- Java中synchronized和ReentrantLock的區(qū)別對(duì)比
- 深入理解Java中的synchronized 和 ReentrantLock 底層原理
- Java之synchronized(含與ReentrantLock的區(qū)別解讀)
- Java中的synchronized和ReentrantLock的區(qū)別詳細(xì)解讀
- 關(guān)于synchronized、volatile、ReentrantLock的區(qū)別與對(duì)比
- Synchronized?和?ReentrantLock?的實(shí)現(xiàn)原理及區(qū)別
- Java常用鎖synchronized和ReentrantLock的區(qū)別
- Java線程安全解決方案(synchronized,ReentrantLock,Atomic)
- 深入理解java內(nèi)置鎖(synchronized)和顯式鎖(ReentrantLock)
相關(guān)文章
SpringBoot利用redis集成消息隊(duì)列的方法
這篇文章主要介紹了SpringBoot利用redis集成消息隊(duì)列的方法,需要的朋友可以參考下2017-08-08
Java長(zhǎng)字符串加密的實(shí)現(xiàn)
在Java中,字符串是一種常見(jiàn)的數(shù)據(jù)類(lèi)型,我們經(jīng)常需要對(duì)其進(jìn)行加密和解密,本文主要介紹了Java長(zhǎng)字符串加密的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02
springboot使用redisTemplate操作lua腳本
本文主要介紹了springboot使用redisTemplate操作lua腳本,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
springcloud使用feign調(diào)用服務(wù)時(shí)參數(shù)內(nèi)容過(guò)大問(wèn)題
這篇文章主要介紹了springcloud使用feign調(diào)用服務(wù)時(shí)參數(shù)內(nèi)容過(guò)大問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
MyBatisPlus用JavaBean映射數(shù)據(jù)庫(kù)表及樂(lè)觀鎖實(shí)戰(zhàn)指南
本文主要介紹了MyBatis-Plus查詢中映射匹配兼容性相關(guān)概念,以及當(dāng)表字段與編碼屬性設(shè)計(jì)不同步時(shí)的解決辦法和JavaBean在MyBatis-Plus中的作用;同時(shí)講述了主鍵id生成策略控制、多記錄操作、邏輯刪除以及樂(lè)觀鎖等操作的實(shí)現(xiàn)方式2026-04-04
springcloud項(xiàng)目里application.yml不加載的坑及解決
這篇文章主要介紹了springcloud項(xiàng)目里application.yml不加載的坑及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
java實(shí)現(xiàn)線程調(diào)度器和時(shí)間分片
線程調(diào)度器和時(shí)間分片是多線程編程和操作系統(tǒng)設(shè)計(jì)中的核心概念,本文主要介紹了java實(shí)現(xiàn)線程調(diào)度器和時(shí)間分片,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-10-10
Java實(shí)現(xiàn)讀取Jar文件屬性的方法詳解
這篇文章主要為大家詳細(xì)介紹了如何利用Java語(yǔ)言實(shí)現(xiàn)讀取Jar文件屬性的功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-08-08
Mybatis控制臺(tái)打印Sql語(yǔ)句的實(shí)現(xiàn)代碼
MyBatis是一個(gè)支持普通SQL查詢,存儲(chǔ)過(guò)程和高級(jí)映射的優(yōu)秀持久層框架,下面給大家介紹Mybatis控制臺(tái)打印Sql語(yǔ)句的實(shí)現(xiàn)代碼,非常不錯(cuò),感興趣的朋友一起看下吧2016-07-07

