鏈接(原文鏈接):https://blog.csdn.net/u012545728/article/details/80843595
最近正在閱讀Java ReentrantLock源碼,始終對可重入和不可重入概念理解不透徹,進行學習後記錄在這裏。
基礎知識
Java多線程的wait()方法和notify()方法
這兩個方法是成對出現和使用的,要執行這兩個方法,有一個前提就是,當前線程必須獲其對象的monitor(俗稱“鎖”),否則會拋出IllegalMonitorStateException異常,所以這兩個方法必須在同步塊代碼裏面調用。
wait():阻塞當前線程
notify():喚起被wait()阻塞的線程
不可重入鎖
所謂不可重入鎖,即若當前線程執行某個方法已經獲取了該鎖,那麼在方法中嘗試再次獲取鎖時,就會獲取不到被阻塞。我們嘗試設計一個不可重入鎖:
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();
}
}
使用該鎖:
public class Count{
Lock lock = new Lock();
public void print(){
lock.lock();
doAdd();
lock.unlock();
}
public void doAdd(){
lock.lock();
//do something
lock.unlock();
}
}
當前線程執行print()方法首先獲取lock,接下來執行doAdd()方法就無法執行doAdd()中的邏輯,必須先釋放鎖。這個例子很好的說明了不可重入鎖。
可重入鎖
接下來,我們設計一種可重入鎖
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();
}
}
}
}
所謂可重入,意味着線程可以進入它已經擁有的鎖的同步代碼塊兒。
我們設計兩個線程調用print()方法,第一個線程調用print()方法獲取鎖,進入lock()方法,由於初始lockedBy是null,所以不會進入while而掛起當前線程,而是是增量lockedCount並記錄lockBy爲第一個線程。接着第一個線程進入doAdd()方法,由於同一進程,所以不會進入while而掛起,接着增量lockedCount,當第二個線程嘗試lock,由於isLocked=true,所以他不會獲取該鎖,直到第一個線程調用兩次unlock()將lockCount遞減爲0,纔將標記爲isLocked設置爲false。
可重入鎖的概念和設計思想大體如此,Java中的可重入鎖ReentrantLock設計思路也是這樣
END