一篇文章讓你徹底了解Java可重入鎖和不可重入鎖
可重入鎖
廣義上的可重入鎖指的是可重復(fù)可遞歸調(diào)用的鎖,在外層使用鎖之后,在內(nèi)層仍然可以使用,并且不發(fā)生死鎖(前提得是同一個(gè)對(duì)象或者class),這樣的鎖就叫做可重入鎖。
我的理解就是,某個(gè)線程已經(jīng)獲得某個(gè)鎖,可以無(wú)需等待而再次獲取鎖,并且不會(huì)出現(xiàn)死鎖(不同線程當(dāng)然不能多次獲得鎖,需要等待)。
簡(jiǎn)單的說(shuō),就是某個(gè)線程獲得某個(gè)鎖,之后可以不用等待而再次獲取鎖且不會(huì)出現(xiàn)死鎖。
常見(jiàn)的可重入鎖
Synchronized和ReentrantLock 都是可重入鎖。
可重入鎖的釋放
同一個(gè)線程獲取同一個(gè)鎖,狀態(tài)值state會(huì)累加,假設(shè)state累加到了2,每釋放一次鎖會(huì)減1,只有當(dāng)狀態(tài)值state減到0了,其他線程才有機(jī)會(huì)獲取鎖。也就是說(shuō),state歸零才是已釋放鎖的標(biāo)致。
可重入鎖示例
public class ReentrantTest implements Runnable {
@Override
public void run() {
get();
}
public synchronized void get() {
System.out.println(Thread.currentThread().getName());
set();
}
/**
* 遞歸方法
*/
public synchronized void set() {
System.out.println(Thread.currentThread().getName());
}
/**
* 這里的對(duì)象鎖只有一個(gè),就是rt對(duì)象的鎖。
* 當(dāng)執(zhí)行rt的get方法時(shí),該線程獲得rt對(duì)象的鎖。在get方法內(nèi)執(zhí)行set方法時(shí)再次請(qǐng)求rt對(duì)象的鎖,因?yàn)閟ynchronized是可重入鎖,所以又可以得到該鎖。循環(huán)這個(gè)過(guò)程。
* 假設(shè)不是可重入鎖的話,那么請(qǐng)求的過(guò)程中會(huì)出現(xiàn)阻塞,從而導(dǎo)致死鎖。
* @param args
*/
public static void main(String[] args) {
ReentrantTest rt = new ReentrantTest();
// for(;;)模擬無(wú)限循環(huán)
for(;;){
new Thread(rt).start();
}
}
}
分析:這里的對(duì)象鎖只有一個(gè),就是rt對(duì)象的鎖。當(dāng)執(zhí)行rt的get方法時(shí),該線程獲得rt對(duì)象的鎖。在get方法內(nèi)執(zhí)行set方法時(shí)再次請(qǐng)求rt對(duì)象的鎖,因?yàn)閟ynchronized是可重入鎖,所以又可以得到該鎖。循環(huán)這個(gè)過(guò)程。假設(shè)不是可重入鎖的話,那么請(qǐng)求的過(guò)程中會(huì)出現(xiàn)阻塞,從而導(dǎo)致死鎖。
死鎖
多線程中,不同的線程都在等待其它線程釋放鎖,而其它線程由于一些原因遲遲沒(méi)有釋放鎖。程序的運(yùn)行處于阻塞狀態(tài),不能正常運(yùn)行也不能正常終止。
運(yùn)行結(jié)果

set()和get()同時(shí)輸出了相同的線程名稱(chēng),也就是說(shuō)某個(gè)線程執(zhí)行的時(shí)候,不僅進(jìn)入了set同步方法,還進(jìn)入了get同步方法。遞歸使用synchronized也沒(méi)有發(fā)生死鎖,證明其是可重入的。
可重入鎖的實(shí)現(xiàn)原理?
每一個(gè)鎖關(guān)聯(lián)一個(gè)線程持有者和計(jì)數(shù)器,當(dāng)計(jì)數(shù)器為 0 時(shí)表示該鎖沒(méi)有被任何線程持有,那么任何線程都可能獲得該鎖而調(diào)用相應(yīng)的方法;當(dāng)某一線程請(qǐng)求成功后,JVM會(huì)記下鎖的持有線程,并且將計(jì)數(shù)器置為 1;此時(shí)其它線程請(qǐng)求該鎖,則必須等待;而該持有鎖的線程如果再次請(qǐng)求這個(gè)鎖,就可以再次拿到這個(gè)鎖,同時(shí)計(jì)數(shù)器會(huì)遞增1;當(dāng)線程退出同步代碼塊時(shí),計(jì)數(shù)器會(huì)遞減1,如果計(jì)數(shù)器為 0,則釋放該鎖。
再分析一下上面可重入鎖的例子
遞歸調(diào)用一次同步代碼塊,計(jì)數(shù)器會(huì)變?yōu)?,整個(gè)遞歸調(diào)用執(zhí)行完,先退出內(nèi)層執(zhí)行減1,再退出外層執(zhí)行減1。然后釋放鎖。
不可重入鎖
就是某個(gè)線程已經(jīng)獲得某個(gè)鎖,之后不可以再次獲取鎖,會(huì)被阻塞。
設(shè)計(jì)一個(gè)不可重入鎖
public class Lock {
private boolean isLocked = false;
/**
* 加鎖
*/
public synchronized void lock() throws Exception{
while(isLocked){
//當(dāng)前線程釋放鎖,讓出CPU,進(jìn)入等待狀態(tài),直到被喚醒,才繼續(xù)執(zhí)行15行
wait();
System.out.println("wait");
}
isLocked = true;
}
/**
* 解鎖
*/
public synchronized void unlock(){
isLocked = false;
//喚醒一個(gè)等待的線程繼續(xù)執(zhí)行
notify();
}
}
測(cè)試
public class Test {
Lock lock = new Lock();
public void print() throws Exception{
//加鎖 標(biāo)記為true
lock.lock();
//釋放鎖->等待 阻塞在16行
doAdd();
lock.unlock();
}
public void doAdd() throws Exception{
lock.lock();
System.out.println("doAdd");
lock.unlock();
}
public static void main(String[] args)throws Exception {
Test test=new Test();
test.print();
}
}
結(jié)果:這里,雖然模擬的是不可重入鎖,實(shí)際還是在單線程環(huán)境中的。當(dāng)前線程執(zhí)行print()方法首先加鎖 標(biāo)記為true,接下來(lái)釋放鎖->等待 阻塞在16行內(nèi)部的14行。整個(gè)過(guò)程中,第一次進(jìn)入lock同步方法,執(zhí)行完畢,第二次進(jìn)入lock同步方法,阻塞等待。這個(gè)例子很好的說(shuō)明了不可重入鎖。
到此這篇關(guān)于一篇文章讓你徹底了解Java可重入鎖和不可重入鎖的文章就介紹到這了,更多相關(guān)Java可重入鎖和不可重入鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Spring Data JPA系列之投影(Projection)的用法
本篇文章主要介紹了詳解Spring Data JPA系列之投影(Projection)的用法,具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07
Jenkins發(fā)送測(cè)試報(bào)告郵件過(guò)程詳解
這篇文章主要介紹了Jenkins發(fā)送測(cè)試報(bào)告郵件過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
Java 程序設(shè)計(jì)總復(fù)習(xí)題(java基礎(chǔ)代碼)
這篇文章主要介紹了Java 程序設(shè)計(jì)總復(fù)習(xí)題,主要是java基礎(chǔ)代碼,方便學(xué)習(xí)java的同學(xué)2021-05-05
java 靜態(tài)工廠代替多參構(gòu)造器的適用情況與優(yōu)劣
這篇文章主要介紹了java 靜態(tài)工廠代替多參構(gòu)造器的優(yōu)劣,幫助大家更好的理解和使用靜態(tài)工廠方法,感興趣的朋友可以了解下2020-12-12
java遞歸處理單位人員組織機(jī)構(gòu)樹(shù)方式
這篇文章主要介紹了java遞歸處理單位人員組織機(jī)構(gòu)樹(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08

