java并發(fā)編程Lock鎖可重入性與公平性分析
一、相似之處:Lock鎖 vs Synchronized 代碼塊
Lock鎖是一種類似于synchronized 同步代碼塊的線程同步機(jī)制。從Java 5開始java.util.concurrent.locks引入了若干個Lock鎖的實現(xiàn)類,所以通常情況下我們不需要實現(xiàn)自己的鎖,重要的是需要知道如何使用它們,了解它們實現(xiàn)背后的原理。
Lock鎖API的基本使用方法和Synchronized 關(guān)鍵字大同小異,代碼如下
Lock lock = new ReentrantLock(); //實例化鎖
//lock.lock(); //上鎖
boolean locked = lock.tryLock(); //嘗試上鎖
if(locked){
try {
//被鎖定的同步代碼塊,同時只能被一個線程執(zhí)行
}finally {
lock.unlock(); //放在finally代碼塊中,保證鎖一定會被釋放
}
}
synchronized(obj){
//被鎖定的同步代碼塊,同時只能被一個線程執(zhí)行
}
Lock鎖使用看上去麻煩一點(diǎn),但是java默認(rèn)提供了很多Lock鎖,能滿足更多的應(yīng)用場景。比如:基于信號量加鎖、讀寫鎖等等,關(guān)注我的專欄《java并發(fā)編程》,后續(xù)都會介紹。
二、Lock接口中的方法
Lock接口實現(xiàn)方法通常會維護(hù)一個計數(shù)器,當(dāng)計數(shù)器=0的時候資源被釋放,當(dāng)計數(shù)器大于1的時候資源被鎖定。
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
- lock() - 調(diào)用該方法會使鎖定計數(shù)器增加1,如果此時共享資源是空閑的,則將鎖交給調(diào)用該方法的線程。
- unlock() - 調(diào)用該方法使鎖定計數(shù)器減少1,當(dāng)鎖定計數(shù)器為0時,資源被釋放。
- tryLock() - 如果該資源是空閑的,那么調(diào)用該方法將返回true,鎖定計數(shù)器將增加1。如果資源處于被占用狀態(tài),那么該方法返回false,但是線程將不被阻塞。
- tryLock(long timeout, TimeUnit unit) - 按照該方法嘗試獲得鎖,如果資源此時被占用,線程在退出前等待一定的時間段,該時間段由該方法的參數(shù)定義,以期望在此時間內(nèi)獲得資源鎖。
- lockInterruptibly() - 如果資源是空閑的,該方法會獲取鎖,同時允許線程在獲取資源時被其他線程打斷。這意味著,如果當(dāng)前線程正在等待一個鎖,但其他線程要求獲得該鎖,那么當(dāng)前線程將被中斷,并立即返回不會獲得鎖。
三、不同點(diǎn):Lock鎖 vs Synchronized 代碼塊
使用synchronized同步塊和使用Lock API 之間還是有一些區(qū)別的
- 一個synchronized同步塊必須完全包含在一個方法中 - 但Lock API的lock()和unlock()操作,可以在不同的方法中進(jìn)行
- synchronized同步塊不支持公平性原則,任何線程都可以在釋放后重新獲得鎖,不能指定優(yōu)先級。但我們可以通過指定fairness 屬性在Lock API中實現(xiàn)公平的優(yōu)先級,可以實現(xiàn)等待時間最長的線程被賦予對鎖的占有權(quán)。
- 如果一個線程無法訪問synchronized同步塊,它就會被阻塞等待。Lock API提供了tryLock()方法,嘗試獲取鎖對象,獲取到鎖返回true,否則返回false。返回false并不阻塞線程,所以使用該方法可以減少等待鎖的線程的阻塞時間。
四、鎖的可重入性
”可重入“意味著某個線程可以安全地多次獲得同一個鎖對象,而不會造成死鎖。
4.1. synchronized鎖的可重入性
下面的代碼synchronized代碼塊嵌套synchronized代碼塊,鎖定同一個this對象,不會產(chǎn)生死鎖。證明synchronized代碼塊針對同一個對象加鎖,是可重入的。
public void testLock(){
synchronized (this) {
System.out.println("第1次獲取鎖,鎖對象是:" + this);
int index = 1;
do {
synchronized (this) {
System.out.println("第" + (++index) + "次獲取鎖,鎖對象是:" + this);
}
} while (index != 10);
}
}
上面的這段代碼輸出結(jié)果是
第1次獲取鎖,鎖對象是:com.example.demo.thread.TestLockReentrant@769c9116 第2次獲取鎖,鎖對象是:com.example.demo.thread.TestLockReentrant@769c9116 第3次獲取鎖,鎖對象是:com.example.demo.thread.TestLockReentrant@769c9116 第4次獲取鎖,鎖對象是:com.example.demo.thread.TestLockReentrant@769c9116 第5次獲取鎖,鎖對象是:com.example.demo.thread.TestLockReentrant@769c9116 第6次獲取鎖,鎖對象是:com.example.demo.thread.TestLockReentrant@769c9116 第7次獲取鎖,鎖對象是:com.example.demo.thread.TestLockReentrant@769c9116 第8次獲取鎖,鎖對象是:com.example.demo.thread.TestLockReentrant@769c9116 第9次獲取鎖,鎖對象是:com.example.demo.thread.TestLockReentrant@769c9116 第10次獲取鎖,鎖對象是:com.example.demo.thread.TestLockReentrant@769c9116
4.2.ReentrantLock可重入鎖
Lock接口的實現(xiàn)類ReentrantLock,也是可重入鎖。一般來說類名包含Reentrant的Lock接口實現(xiàn)類實現(xiàn)的鎖都是可重入的。
public void testLock1(){
Lock lock = new ReentrantLock(); //實例化鎖
lock.lock(); //上鎖
System.out.println("第1次獲取鎖,鎖對象是:" + lock);
try {
int index = 1;
do {
lock.lock(); //上鎖
try {
System.out.println("第" + (++index) + "次獲取鎖,鎖對象是:" + lock);
}finally {
lock.unlock();
}
} while (index != 10);
}finally {
lock.unlock(); //放在finally代碼塊中,保證鎖一定會被釋放
}
}
當(dāng)線程第一次獲得鎖的時候,計數(shù)器被設(shè)置為1。在解鎖之前,該線程可以再次獲得鎖,每次計數(shù)器都會增加1。對于每一個解鎖操作,計數(shù)器被遞減1,當(dāng)計數(shù)器為0時鎖定資源被釋放。所以最重要的是:lock(tryLock)要與unlock方法成對出現(xiàn),即:在代碼中加鎖一次就必須解鎖一次,否則就死鎖
五、Lock鎖的公平性
Java的synchronized 同步塊對試圖進(jìn)入它們的線程,被授予訪問權(quán)(占有權(quán))的優(yōu)先級順序沒有任何保證。因此如果許多線程不斷爭奪對同一個synchronized 同步塊的訪問權(quán),就有可能有一個或多個線程從未被授予訪問權(quán)。這就造成了所謂的 "線程饑餓"。為了避免這種情況,鎖應(yīng)該是公平的。
Lock lock = new ReentrantLock(true);
可重入鎖提供了一個公平性參數(shù)fairness ,通過該參數(shù)Lock鎖將遵守鎖請求的順序,即在一個線程解鎖資源后,鎖將被交給等待時間最長的線程。這種公平模式是通過在鎖的構(gòu)造函數(shù)中傳遞 "true "來設(shè)置的。
以上就是java并發(fā)編程Lock鎖可重入性與公平性分析的詳細(xì)內(nèi)容,更多關(guān)于并發(fā)Lock鎖可重入性公平性的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
簡單談?wù)凷pringMVC轉(zhuǎn)發(fā)和重定向的區(qū)別
下面小編就為大家?guī)硪黄唵握務(wù)凷pringMVC轉(zhuǎn)發(fā)和重定向的區(qū)別。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06
Spring Boot集成SpringFox 3.0與Pageable參數(shù)處理方法
這篇文章主要介紹了Spring Boot集成SpringFox 3.0與Pageable參數(shù)處理,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-10-10
SpringMVC用XML方式實現(xiàn)AOP的方法示例
這篇文章主要介紹了SpringMVC用XML方式實現(xiàn)AOP的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
SpringBoot項目的漏洞修復(fù)經(jīng)驗分享
在局域網(wǎng)環(huán)境下,由于無法連接外網(wǎng)下載Maven包,常見解決方案是在外網(wǎng)環(huán)境搭建相同的開發(fā)環(huán)境以便更新Maven包,本次漏洞掃描包括Tomcat、jackson-databind、fastjson、logback等組件,通常解決方法是升級到更高版本2024-10-10
使用Logback設(shè)置property參數(shù)方式
這篇文章主要介紹了使用Logback設(shè)置property參數(shù)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03

