了解可重入鎖和不可重入鎖的區(qū)別及原理嗎

首先我們這里提到的鎖,是把所需要的代碼塊,資源,或數(shù)據(jù)鎖上,在操作他們的時候只允許一個線程去做操作。最終結(jié)果是為了保證cpu計算結(jié)果的正確性。
對不可重入鎖的理解:
public class Test{
Lock lock = new Lock();
public void methodA(){
lock.lock();
...........;
methodB();
...........;
lock.unlock();
}
public void methodB(){
lock.lock();
...........;
lock.unlock();
}
}
當A方法獲取lock鎖去鎖住一段需要做原子性操作的B方法時,如果這段B方法又需要鎖去做原子性操作,那么A方法就必定要與B方法出現(xiàn)死鎖。這種會出現(xiàn)問題的重入一把鎖的情況,叫不可重入鎖。
A方法需要等B方法執(zhí)行完才能解鎖,但是B方法想執(zhí)行完代碼又必須要lock鎖來加鎖。A的鎖未解鎖前,其他代碼塊無法使用此鎖來加鎖。這是由這個不可重入鎖決定的。
不可重入鎖:
public class Lock{
private boolean isLocked = false;
public synchronized void lock() throws InterruptedException{
while(isLocked){
wait();
}
isLocked = true;
}
public synchronized void unlock(){
isLocked = false;
notify();
}
}
那么平時我們又有需要重入一把鎖的需求!!!!比如A方法是個原子性操作,但它有需要調(diào)用B方法的原子性操作,他們還爭搶的是同一個臨界資源,因此需要同一把鎖來加鎖(ps:爭搶同一臨界資源的實質(zhì)就是對同一把鎖的爭搶)
針對此情況,就有了可重入鎖的概念:
可重入鎖的實現(xiàn):
public class Lock{
boolean isLocked = false;
Thread lockedBy = null;
int lockedCount = 0;
public synchronized void lock()
throws InterruptedException{
Thread thread = Thread.currentThread();
while(isLocked && lockedBy != thread){
wait();
}
isLocked = true;
lockedCount++;
lockedBy = thread;
}
public synchronized void unlock(){
if(Thread.currentThread() == this.lockedBy){
lockedCount--;
if(lockedCount == 0){
isLocked = false;
notify();
}
}
}
}
可以看見代碼的核心概念是:
首先解釋lockedBy:顧名思義,臨界資源被哪個線程鎖住了。
加鎖時,先獲取當前線程。(識別誰需要鎖)
Thread thread = Thread.currentThread();
判斷:當臨界資源已被鎖上,但當前請求鎖的線程又不是之前鎖上臨界資源的線程。那么當前請求鎖的線程需要等待。
while(isLocked && lockedBy != thread){
wait();
}
注意上面是個while,并且是個wait,因此當請求線程請求不到鎖的時候,就wait了。
當時當while不滿足有的3種情況:
A:當前鎖沒有線程使用.
B:當前鎖有線程使用,當前請求鎖的線程就是現(xiàn)在正在使用鎖的線程。
C:當前鎖沒有線程使用,當前請求鎖的線程就是現(xiàn)在正在使用鎖的線程。(不可能出現(xiàn),鎖0沒有被用,哪還有線程使用鎖)
來看看
A:沒有線程使用:
那么:
isLocked = true;
lockedCount++;
lockedBy = thread;
當前請求鎖的線程先把鎖加上,然后把上鎖次數(shù)+1,然后把自己(本線程)賦值給lockedBy,以說明當前誰用了這把鎖方便之后重入的時候做while判斷。
再來看解鎖:
public synchronized void unlock(){
if(Thread.currentThread() == this.lockedBy){
lockedCount--;
if(lockedCount == 0){
isLocked = false;
notify();
}
}
}
首先看看要求解鎖的線程是不是當前用鎖的線程。不是則什么也不做。(當然不能隨意讓其他的線程一執(zhí)行unlock代碼就能解鎖使用啊。那這樣相當于誰都有一把鑰匙了,這里這個判斷也就是說明解鎖的必須是加鎖的)
如果要求解鎖的就是加鎖的線程。
那么把加鎖次數(shù)減一。
然后在判斷加鎖次數(shù)有沒有變?yōu)?。
變?yōu)?說明,這個鎖已經(jīng)完全解鎖了。鎖上標識islocked可以復(fù)位了。
并且隨機喚醒某個被wait()等待的線程 :notify()
它和不可重入鎖的設(shè)計不同之處:
不可重入鎖:只判斷這個鎖有沒有被鎖上,只要被鎖上申請鎖的線程都會被要求等待。實現(xiàn)簡單 可重入鎖:不僅判斷鎖有沒有被鎖上,還會判斷鎖是誰鎖上的,當就是自己鎖上的時候,那么他依舊可以再次訪問臨界資源,并把加鎖次數(shù)加一。
設(shè)計了加鎖次數(shù),以在解鎖的時候,可以確保所有加鎖的過程都解鎖了,其他線程才能訪問。不然沒有加鎖的參考值,也就不知道什么時候解鎖?解鎖多少次?才能保證本線程已經(jīng)訪問完臨界資源了可以喚醒其他線程訪問了。實現(xiàn)相對復(fù)雜。
總結(jié)
這個重入的概念就是,拿到鎖的代碼能不能多次以不同的方式訪問臨界資源而不出現(xiàn)死鎖等相關(guān)問題。經(jīng)典之處在于判斷了需要使用鎖的線程是否為加鎖的線程。如果是,則擁有重(chong)入的能力。
