可重入讀寫鎖原理分析
阿新 • • 發佈:2019-01-08
前段時間看了一系列併發程式設計部落格,感覺寫的不錯。這裡記錄一下其中可重入讀寫鎖的自己實現方法,雖然java中都有封裝好的讀寫鎖可用,但分析一下程式碼有助於理解鎖機制的原理。
部落格原地址
package cn.wcl.readWriteLock;
import java.util.HashMap;
import java.util.Map;
/**
* @author www
* 可重入讀寫鎖demo
* 【只是一種模擬,用於理解可重入讀寫鎖的原理,java中有已經封裝好的鎖可以使用,無需自己設計鎖】
* 基於情況:寫操作比讀操作優先順序高,讀操作比寫操作頻繁得多。
* 同一時間可以有多個執行緒對資源進行讀取,但獲取寫鎖的執行緒是唯一的
* 獲取讀鎖的條件:沒有執行緒正在做寫操作,且沒有執行緒在請求寫操作
* 獲取寫鎖的條件:沒有執行緒正在做讀寫操作
* @time :2017年4月26日 下午5:06:46
*/
public class ReadWriteLock {
/**
* 擁有讀鎖的Thread:用一個map來儲存已經持有讀鎖的執行緒以及對應執行緒獲取讀鎖的次數(用於可重入鎖統計重入次數)
*/
private Map<Thread, Integer> readingThreads = new HashMap<Thread, Integer>();
/**
* 寫鎖的個數
*/
private int writeAccesses = 0;
/**
* 申請寫鎖的執行緒數
*/
private int writeRequest = 0;
/**
* 當前獲得寫鎖的執行緒
*/
private Thread writingThread = null;
public synchronized void lockRead() throws InterruptedException {
Thread callingThread = Thread.currentThread();
while(!canGrantReadAccess(callingThread)) {
wait();
}
readingThreads.put(callingThread, getReadAccessCount(callingThread) + 1 );
}
/**
* @author www
* 是否能獲得讀鎖許可
* @param callingThread
* @return
*/
private boolean canGrantReadAccess(Thread callingThread) {
//注意條件的順序,代表了它們的優先順序
if(isWriter(callingThread)) return true;//如果當前執行緒在寫,那麼該執行緒也可以讀
if(hasWriter()) return false;//有其他執行緒在寫,那麼當前執行緒不能讀
if(isReader(callingThread)) return true;//當前執行緒已經獲得讀鎖,可以重入
if(hasWriterRequests()) return false;//當前執行緒沒有獲得讀鎖,有其他執行緒請求寫鎖,則不能讀,因為讀優先順序低
//沒有寫鎖請求,可能有其他執行緒在讀,也能沒有,當前執行緒可以獲得讀鎖,因為多個執行緒可以一起讀
return true;
}
public synchronized void unlockRead() {
Thread callingThread = Thread.currentThread();
if(!isReader(callingThread)) {
throw new IllegalMonitorStateException("callingThread未持有讀鎖!");
}
int readAccessCount = getReadAccessCount(callingThread);
if(readAccessCount == 1) {
readingThreads.remove(callingThread);
} else {
//如果重入過,重入次數-1
readingThreads.put(callingThread, readAccessCount - 1);
}
//用notifyAll()!
//因為讀操作可以所有執行緒一起進行,只notify()喚醒一個,會導致只有一個等待的讀執行緒獲得鎖,效率低
notifyAll();
}
public synchronized void lockWrite() throws InterruptedException {
writeRequest ++;
Thread callingThread = Thread.currentThread();
while(!canGrantWriteAccess(callingThread)) {
wait();
}
writeAccesses ++;
writeRequest --;
writingThread = callingThread;
}
private boolean canGrantWriteAccess(Thread callingThread) {
//順序很有意思
if(isOnlyReader(callingThread)) return true;//只有該執行緒在讀,則該執行緒也可以寫
if(hasReaders()) return false;//有其他執行緒在讀,該執行緒就不能寫;這條必須在第一條後面驗證
if(writingThread == null) return true;//沒有執行緒在讀,也沒有執行緒在寫,該執行緒可以獲得寫鎖
if(!isWriter(callingThread)) return false;//沒有執行緒在讀,有執行緒在寫,且寫執行緒不是當前執行緒,則當前執行緒不能寫
return true;//沒有執行緒在讀,當前執行緒在寫,則當前執行緒重入寫鎖
}
private synchronized void unlockWrite() {
Thread callingThread = Thread.currentThread();
if(!isWriter(callingThread)) {
throw new IllegalMonitorStateException("callingThread未持有寫鎖!");
}
writeAccesses --;
/*writingThread = null;*/ //不能直接置為null,因為有可能是重入鎖
if(writeAccesses == 0) {
writingThread = null;
}
notifyAll();
}
private boolean hasReaders() {
return !readingThreads.isEmpty();
}
private boolean isOnlyReader(Thread callingThread) {
return readingThreads.size() == 1 && readingThreads.containsKey(callingThread);
}
private boolean hasWriterRequests() {
return writeRequest > 0;
}
private boolean isReader(Thread callingThread) {
return readingThreads.containsKey(callingThread);
}
private boolean hasWriter() {
return writingThread != null;
}
private boolean isWriter(Thread callingThread) {
return writingThread == callingThread;
}
private int getReadAccessCount(Thread callingThread) {
Integer count = readingThreads.get(callingThread);
if(count == null) {
return 0;
}
return count.intValue();
}
}