鎖的可重入性是為了防止已經獲得鎖的線程再次獲得鎖可能會帶來的死鎖問題,比如:
public synchronized void A(){
B();
}
public synchronized void B(){
}
如果Java內置的synchronized
鎖不是可重入的,則線程A在獲得鎖進入A()
后調用B()
時,需要等待線程A釋放鎖,而此時線程A還沒有完成A()
,則不會釋放鎖,因此產生死鎖。
一個簡單的可重入鎖實現
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* 簡單的可重入鎖實現:
* 使用一個計數器記錄當前線程重入鎖的次數,獲得鎖時計數器加1,
* 釋放鎖時計數器減1,當計數器等于0時表示釋放了鎖
**/
public class SimpleReentrantLock implements Lock{
// 指向已經獲得鎖的線程
private volatile Thread exclusiveOwnerThread;
// 記錄獲取了同一個鎖的次數
private volatile int holdCount;
private final java.util.concurrent.locks.Lock lock;
// 是否是自己獲得鎖的條件
private final Condition isCountZero;
public SimpleReentrantLock(){
lock = new ReentrantLock();
isCountZero = lock.newCondition();
holdCount = 0;
}
@Override
public void lock() {
lock.lock();
try{
// 當前線程的引用
Thread currentThread = Thread.currentThread();
// 如果獲得鎖的線程是自己,那么計數器加1,直接返回
if(exclusiveOwnerThread == currentThread){
holdCount ++;
return;
}
while(holdCount != 0){
try {
isCountZero.await();
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted");
}
}
// 將exclusiveOwnerThread設置為自己
exclusiveOwnerThread = currentThread;
holdCount ++;
}finally{
lock.unlock();
}
}
@Override
public void unlock() {
lock.lock();
try{
holdCount --;
if(holdCount == 0){
isCountZero.signalAll();
}
}finally{
lock.unlock();
}
}
}
- 使用一個Thread引用指向獲得鎖的線程
- 使用一個計數器記錄一個線程進入鎖的次數,當計數器為0時表示鎖是空閑的
- 使用一個內部鎖Lock來同步線程
- 使用一個isHoldZero的條件來進行條件隊列操作
- 當獲得鎖的線程是自己時,只修改計數器的值,直接獲得鎖
- 當獲得鎖的線程不是自己時,需要在holdCount !=0 這個條件謂詞上等待,直到計數器歸0,再次競爭鎖
- 釋放鎖時計數器減1,當計數器為0時,喚醒在條件隊列中等待的線程
問題1:為什么鎖要具備可重入性?