1. 程式人生 > >可重入讀寫鎖原理分析

可重入讀寫鎖原理分析

前段時間看了一系列併發程式設計部落格,感覺寫的不錯。這裡記錄一下其中可重入讀寫鎖的自己實現方法,雖然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(); } }