Java ReentrantLock的使用與應(yīng)用實(shí)戰(zhàn)
ReentrantLock是Java并發(fā)包(java.util.concurrent.locks)中提供的一種可重入互斥鎖,它作為synchronized關(guān)鍵字的替代方案,提供了更靈活、更強(qiáng)大的線程同步機(jī)制。本文將全面解析ReentrantLock的核心特性、實(shí)現(xiàn)原理及實(shí)際應(yīng)用場景。
ReentrantLock概述與基本特性
ReentrantLock是Java 5引入的顯式鎖機(jī)制,它基于AQS(AbstractQueuedSynchronizer)框架實(shí)現(xiàn),提供了比synchronized更豐富的功能和控制能力。與synchronized相比,ReentrantLock具有以下顯著特點(diǎn):
- ?可重入性?:同一線程可以多次獲得同一把鎖而不會被阻塞,每次獲取鎖后必須釋放相同次數(shù)的鎖
- ?公平性選擇?:支持公平鎖和非公平鎖兩種策略,公平鎖按照線程請求順序分配鎖,非公平鎖允許"插隊(duì)"以提高吞吐量
- ?靈活的鎖獲取方式?:提供嘗試非阻塞獲取鎖(tryLock)、可中斷獲取鎖(lockInterruptibly)和超時獲取鎖(tryLock with timeout)等方法
- ?條件變量支持?:通過Condition接口實(shí)現(xiàn)多個等待隊(duì)列,比synchronized的wait/notify機(jī)制更精準(zhǔn)
從實(shí)現(xiàn)層級看,synchronized是JVM內(nèi)置的鎖機(jī)制,通過monitorenter/monitorexit字節(jié)碼指令實(shí)現(xiàn);而ReentrantLock是JDK API級別的鎖,基于AQS框架構(gòu)建。
ReentrantLock核心方法與使用
基礎(chǔ)鎖操作
ReentrantLock的基本使用模式遵循"加鎖-操作-釋放鎖"的流程,必須確保在finally塊中釋放鎖:
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// 臨界區(qū)代碼
} finally {
lock.unlock();
}
這種顯式鎖管理相比synchronized需要更多注意,但提供了更精細(xì)的控制。
高級鎖獲取方式
?嘗試非阻塞獲取鎖(tryLock)??:
立即返回獲取結(jié)果,不阻塞線程,適用于避免死鎖或快速失敗場景:
if (lock.tryLock()) { try { // 臨界區(qū)代碼 } finally { lock.unlock(); } } else { // 執(zhí)行備選方案 }?超時獲取鎖?:
在指定時間內(nèi)嘗試獲取鎖,避免無限期等待:
if (lock.tryLock(2, TimeUnit.SECONDS)) { try { // 臨界區(qū)代碼 } finally { lock.unlock(); } }?可中斷獲取鎖(lockInterruptibly)??:
允許在等待鎖的過程中響應(yīng)中斷信號:
try { lock.lockInterruptibly(); try { // 臨界區(qū)代碼 } finally { lock.unlock(); } } catch (InterruptedException e) { // 處理中斷 }
鎖狀態(tài)查詢
ReentrantLock提供了一系列狀態(tài)查詢方法:
isLocked():查詢鎖是否被持有isHeldByCurrentThread():當(dāng)前線程是否持有鎖getHoldCount():當(dāng)前線程持有鎖的次數(shù)(重入次數(shù))getQueueLength():等待獲取鎖的線程數(shù)
ReentrantLock實(shí)現(xiàn)原理
AQS框架基礎(chǔ)
ReentrantLock的核心實(shí)現(xiàn)依賴于AbstractQueuedSynchronizer(AQS),這是一個用于構(gòu)建鎖和同步器的框架。AQS內(nèi)部維護(hù)了:
- volatile int state:同步狀態(tài),對于ReentrantLock,0表示未鎖定,>0表示鎖定狀態(tài)及重入次數(shù)
- FIFO線程等待隊(duì)列:管理獲取鎖失敗的線程
公平鎖與非公平鎖實(shí)現(xiàn)
ReentrantLock通過兩種不同的Sync子類實(shí)現(xiàn)鎖策略:
?非公平鎖(默認(rèn))??:
final void lock() { if (compareAndSetState(0, 1)) // 直接嘗試搶占 setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }新請求的線程可以直接插隊(duì)嘗試獲取鎖,不考慮等待隊(duì)列
?公平鎖?:
protected final boolean tryAcquire(int acquires) { if (!hasQueuedPredecessors() && // 檢查是否有前驅(qū)節(jié)點(diǎn) compareAndSetState(0, acquires)) { setExclusiveOwnerThread(Thread.currentThread()); return true; } // 可重入邏輯... }嚴(yán)格按照FIFO順序分配鎖,避免饑餓現(xiàn)象
鎖的獲取與釋放流程
?加鎖過程?:
- 嘗試通過CAS修改state狀態(tài)
- 成功則設(shè)置當(dāng)前線程為獨(dú)占線程
- 失敗則構(gòu)造Node加入CLH隊(duì)列尾部,并阻塞線程
?釋放過程?:
- 減少持有計(jì)數(shù)(state減1)
- 當(dāng)state為0時完全釋放鎖
- 喚醒隊(duì)列中的下一個等待線程
ReentrantLock實(shí)戰(zhàn)應(yīng)用
生產(chǎn)者-消費(fèi)者模型
使用ReentrantLock配合Condition實(shí)現(xiàn)高效的生產(chǎn)者-消費(fèi)者模式:
public class BoundedBuffer {
private final ReentrantLock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private final Object[] items = new Object[100];
private int putPtr, takePtr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await(); // 等待"不滿"條件
items[putPtr] = x;
if (++putPtr == items.length) putPtr = 0;
++count;
notEmpty.signal(); // 通知"不空"條件
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await(); // 等待"不空"條件
Object x = items[takePtr];
if (++takePtr == items.length) takePtr = 0;
--count;
notFull.signal(); // 通知"不滿"條件
return x;
} finally {
lock.unlock();
}
}
}
這種實(shí)現(xiàn)比synchronized+wait/notify更高效,因?yàn)榭梢跃珳?zhǔn)喚醒生產(chǎn)者或消費(fèi)者線程。
銀行轉(zhuǎn)賬避免死鎖
使用tryLock實(shí)現(xiàn)帶超時的轉(zhuǎn)賬操作,避免死鎖:
public boolean transfer(Account from, Account to, int amount, long timeout, TimeUnit unit) {
long stopTime = System.nanoTime() + unit.toNanos(timeout);
while (true) {
if (from.getLock().tryLock()) {
try {
if (to.getLock().tryLock()) {
try {
if (from.getBalance() < amount)
throw new InsufficientFundsException();
from.withdraw(amount);
to.deposit(amount);
return true;
} finally {
to.getLock().unlock();
}
}
} finally {
from.getLock().unlock();
}
}
if (System.nanoTime() > stopTime)
return false;
Thread.sleep(fixedDelay);
}
}
通過tryLock和超時機(jī)制,有效預(yù)防了死鎖風(fēng)險(xiǎn)。
可中斷的任務(wù)執(zhí)行
使用lockInterruptibly實(shí)現(xiàn)可中斷的任務(wù)執(zhí)行:
public class InterruptibleTask {
private final ReentrantLock lock = new ReentrantLock();
public void executeTask() throws InterruptedException {
lock.lockInterruptibly();
try {
// 執(zhí)行可能長時間運(yùn)行的任務(wù)
while (!Thread.currentThread().isInterrupted()) {
// 任務(wù)邏輯...
}
} finally {
lock.unlock();
}
}
}
這種模式適用于需要支持任務(wù)取消的場景。
ReentrantLock與synchronized的對比
| 特性 | synchronized | ReentrantLock |
|---|---|---|
| 實(shí)現(xiàn)層級 | JVM內(nèi)置 | JDK API實(shí)現(xiàn) |
| 鎖釋放 | 自動 | 必須手動調(diào)用unlock() |
| 公平鎖支持 | 僅非公平 | 支持公平和非公平策略 |
| 可中斷獲取鎖 | 不支持 | 支持(lockInterruptibly) |
| 超時獲取鎖 | 不支持 | 支持(tryLock with timeout) |
| 條件變量 | 單一等待隊(duì)列 | 支持多個Condition |
| 鎖狀態(tài)查詢 | 有限 | 提供豐富查詢方法 |
| 性能 | Java 6+優(yōu)化 | 復(fù)雜場景下表現(xiàn)更好 |
| 代碼簡潔性 | 高 | 較低(需手動管理) |
| 適用場景 | 簡單同步 | 復(fù)雜同步需求 |
在Java 6及以后版本中,synchronized經(jīng)過鎖升級(偏向鎖→輕量級鎖→重量級鎖)優(yōu)化,性能與ReentrantLock差距已不明顯。因此,簡單場景推薦使用synchronized,復(fù)雜場景才考慮ReentrantLock。
ReentrantLock最佳實(shí)踐
?始終在finally塊中釋放鎖?:
確保鎖一定會被釋放,避免死鎖:
lock.lock(); try { // 臨界區(qū)代碼 } finally { lock.unlock(); }?避免嵌套鎖?:
盡量不要在持有一個鎖的情況下嘗試獲取另一個鎖,容易導(dǎo)致死鎖。
?合理選擇鎖策略?:
- 高吞吐場景:非公平鎖(默認(rèn))
- 避免饑餓場景:公平鎖
?優(yōu)先使用tryLock?:
特別是涉及多個鎖的操作,使用tryLock可以避免死鎖。
?合理使用Condition?:
替代Object的wait/notify,實(shí)現(xiàn)更精準(zhǔn)的線程通信。
?性能考量?:
簡單同步場景優(yōu)先選擇synchronized,復(fù)雜場景才使用ReentrantLock。
總結(jié)
ReentrantLock作為Java并發(fā)編程中的重要工具,通過其可重入性、公平性選擇、靈活的鎖獲取方式和條件變量支持,為開發(fā)者提供了比synchronized更強(qiáng)大的線程同步能力。理解其基于AQS的實(shí)現(xiàn)原理,掌握各種高級特性的使用方法,并遵循最佳實(shí)踐,可以幫助我們構(gòu)建更高效、更健壯的并發(fā)程序。在實(shí)際開發(fā)中,應(yīng)根據(jù)具體場景需求,在synchronized和ReentrantLock之間做出合理選擇。
到此這篇關(guān)于Java ReentrantLock的使用與應(yīng)用實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)Java ReentrantLock內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java接口統(tǒng)一樣式返回模板的實(shí)現(xiàn)
這篇文章主要介紹了Java接口統(tǒng)一樣式返回模板的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
java servlet結(jié)合Oracle搭建java的web開發(fā)環(huán)境
今天我將與大家分享一下我學(xué)JAVA WEB寫的一些小實(shí)例 ,我個人是不太喜歡書本上的晦澀的概念的,所以我花了更多的時間在一些應(yīng)用實(shí)例上,我覺得這樣的學(xué)習(xí)方式很適合我,由簡到繁,由淺入深2015-12-12
利用Java反射機(jī)制實(shí)現(xiàn)對象相同字段的復(fù)制操作
這篇文章主要介紹了利用Java反射機(jī)制實(shí)現(xiàn)對象相同字段的復(fù)制操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08
SpringBoot請求與響應(yīng)實(shí)例全解析
文章主要介紹了SpringBoot中請求與響應(yīng)的處理方式,包括簡單參數(shù)、實(shí)體參數(shù)、數(shù)組集合參數(shù)、日期參數(shù)、JSON參數(shù)和路徑參數(shù)的處理,同時,還介紹了響應(yīng)的處理,包括@ResponseBody注解和統(tǒng)一響應(yīng)結(jié)果類的設(shè)計(jì)2026-04-04
java時區(qū)時間轉(zhuǎn)為UTC的代碼示例和詳細(xì)解釋
作為一名經(jīng)驗(yàn)豐富的開發(fā)者,我經(jīng)常被問到如何將Java中的時間轉(zhuǎn)換為UTC時間,這篇文章主要介紹了java時區(qū)時間轉(zhuǎn)為UTC的代碼示例和詳細(xì)解釋,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-09-09
mybatis-plus?Wrapper條件構(gòu)造器updateForSet更新方式
這篇文章主要介紹了mybatis-plus?Wrapper條件構(gòu)造器updateForSet更新方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
MyBatis 自動更新時間的方法實(shí)現(xiàn)
本文主要介紹了MyBatis 自動更新時間的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06

