不可重入鎖
先來設計一種鎖
public class Lock{ private boolean isLocked = false; public synchronized void lock() throws InterruptedException{ while(isLocked){ wait();//把當前線程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()方法時,獲得了鎖,這時就無法再調用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){//獲得該鎖的那個線程,獲得了多少次該鎖(即調用了幾次lock方法,即重入了幾次),就得unlock幾次,即lockedCount=0,才會把那些wait(阻塞)的線程喚醒 isLocked = false; notify(); } } } }
相對來說,可重入就意味着:一個線程可以進入任何一個 該線程 已經擁有的鎖所同步着的代碼塊
第一個線程執行print()方法,得到了鎖,使lockedBy等於當前線程,也就是說,執行的這個方法的線程獲得了這個鎖,執行add()方法時,同樣要先獲得鎖(即調用lock.lock()),因不滿足while循環的條件(isLocked(=true,因為該線程調用print()方法時就獲得該鎖了);但是這里與不可重入鎖的區別是有個lockedBy(即表示現在哪個線程持有鎖);因為調用add()方法的是和調用print()的是同一個線程),也就是不等待,繼續進行,將此時的lockedCount變量,也就是當前獲得鎖的數量加一,當釋放了所有的鎖(即得調用獲得鎖數量次數的unlock),才執行notify()。
如果在執行這個方法時,有第二個線程想要執行這個方法,因為lockedBy不等於第二個線程,導致這個線程進入了循環,也就是等待(阻塞),不斷執行wait()方法。只有當第一個線程釋放了所有的鎖(即第一個線程調用了多少次lock()方法就得調用多少次unlock()方法釋放鎖),執行了notify()方法,第二個線程才得以跳出循環,繼續執行。
這就是可重入鎖的特點。
可重入鎖與不可重入鎖對比,簡單來說就是:可重入鎖會多兩個屬性(1、獲得該鎖的線程;2、獲得該鎖的次數),根據第一個屬性判斷,如果是持有該鎖的那個線程又來lock,不會被阻塞(wait),而是在上鎖的次數加一(表示這個線程又鎖了一次(重入)),只有該線程unlock的次數達到上鎖的次數(即第二個屬性等於0),才會喚醒其他線程。
java中常用的可重入鎖:
synchronized
java.util.concurrent.locks.ReentrantLock
https://www.cnblogs.com/dj3839/p/6580765.html