Java的ReadWriteLock實現(xiàn)機制解析
如果接觸過多線程編程或者大規(guī)模并發(fā)應用的開發(fā)的人都應該知道Readers-writer lock的設計模式,從英文字面上看就是對于資源允許多個Reader(復數(shù))并發(fā)讀,單個Writer寫的鎖機制,而Reader和Writer互斥。
現(xiàn)在的JDK里有一個ReadWriteLock的接口和一個ReentrantReadWriteLock的實現(xiàn)類,而其作者是赫赫有名的Doug Lea大牛(他有本 Concurrent Programming in Java Design Principles and Pattern ,推薦一下) 。早在JDK 1.4的時代,他就發(fā)表了自己的cocurrent包實現(xiàn),其中就有多個ReadWriteLock的實現(xiàn)。下面會先聊一下早期Doug Lea在EDU.oswego.cs.dl.util.concurrent版本中對ReadWriteLock的實現(xiàn),***再說JDK版本的。
1.EDU.oswego.cs.dl.util.concurrent的實現(xiàn)
Doug Lea的這個版本:EDU.oswego.cs.dl.util.concurrent包中ReadWriteLock所包含的內容比JDK要豐富不少,除了ReentrantReadWriteLock還有若干個其他實現(xiàn)。先看一下ReadWriteLock在其中的類繼承關系。源代碼下載

這其中包含了4個ReadWriteLock,包括先進先出的FIFOReadWriteLock ,Writer優(yōu)先的WriterPreferenceReadWriteLock ,Reader優(yōu)先的ReaderPreferenceReadWriteLock ,可重入ReentrantWriterPreferenceReadWriteLock 。
1.1 EDU.oswego.cs.dl.util.concurrent.ReadWriteLock接口
Java代碼
- public interface ReadWriteLock {
- /** get the readLock **/
- Sync readLock();
- /** get the writeLock **/
- Sync writeLock();
- }
ReadWriteLock的接口定義和后來JDK的實現(xiàn)基本一致。readLock和writeLock都實現(xiàn)Sync接口,這個接口兩個主要的方法是acquire和realse,在以后的JDK中,變成了Lock的lock方法和unlock方法。
1.2 WriterPreferenceReadWriteLock類
這個類包含了WriterLock類型的writerLock_和ReaderLock類型的readerLock_兩個成員,而這兩個類型又分別為WriterPreferenceReadWriteLock的內部類,這樣做的一個考慮可能是為了能夠讓這兩個類能夠訪問WriterPreferenceReadWriteLock的成員以及互相訪問。
先看看WriterPreferenceReadWriteLock中ReaderLock的實現(xiàn)
該類的幾個成員以及解釋如下
Java代碼
- /*用來給Writer判斷是否可以拿到write控制權*/
- protected long activeReaders_ = 0;
- /*當前writer線程*/
- protected Thread activeWriter_ = null;
- /*可以用來作為策略調整,此版本中沒有太多作用*/
- protected long waitingReaders_ = 0;
- /*等待中的寫,用來給Reader判斷是否還有Writer在等待,以便實現(xiàn)Writer優(yōu)先*/
- protected long waitingWriters_ = 0;
- /*實際的ReaderLock*/
- protected final ReaderLock readerLock_ = new ReaderLock();
- /*實際的WriterLock*/
- protected final WriterLock writerLock_ = new WriterLock();
先來看看 ReaderLock,它的兩個主要方法acquire和release
其中acquire的代碼如下
Java代碼
- public void acquire() throws InterruptedException {
- if (Thread.interrupted())
- throw new InterruptedException();
- InterruptedException ie = null;
- synchronized (this) {
- /**
- * 判斷是否能夠獲得讀權
- */
- if (!startReadFromNewReader()) {
- for (;;) {
- try {
- /**
- * 等待notify
- */
- ReaderLock.this.wait();
- /**
- * 再次判斷能否獲得讀權
- * 因為此處是Writer優(yōu)先,當一個writer釋放時,
- * reader還必須等待其他wait的writer獲得控制權并釋放后才能獲得控制權
- */
- if (startReadFromWaitingReader())
- return;
- } catch (InterruptedException ex) {
- cancelledWaitingReader();
- ie = ex;
- break;
- }
- }
- }
- }
- if (ie != null) {
- // fall through outside synch on interrupt.
- // This notification is not really needed here,
- // but may be in plausible subclasses
- writerLock_.signalWaiters();
- throw ie;
- }
- }
acquire調用startReadFromNewReader,startReadFromWaitingReader,以及allowReader方法,這三個方法都屬于WriterPreferenceReadWriteLock類
Java代碼
- protected synchronized boolean startReadFromNewReader() {
- boolean pass = startRead();
- if (!pass)
- ++waitingReaders_;
- return pass;
- }
- protected synchronized boolean startReadFromWaitingReader() {
- boolean pass = startRead();
- if (pass)
- --waitingReaders_;
- return pass;
- }
- protected boolean allowReader() {
- //通過是否有正有控制權的activeWriter_和等待中的waitingWriters_來判斷是Reader是否能獲得控制權
- return activeWriter_ == null && waitingWriters_ == 0;
- }
- protected synchronized boolean startRead() {
- boolean allowRead = allowReader();
- if (allowRead)
- ++activeReaders_;
- return allowRead;
- }
另外release的代碼如下
Java代碼
- public void release() {
- Signaller s = endRead();
- if (s != null)
- s.signalWaiters();
- }
- protected synchronized Signaller endRead() {
- //只有當沒有Reader在控制以及有等待的Writer的時候才返回
- //因為Reader之間沒有互斥,所以返回writerLock
- if (--activeReaders_ == 0 && waitingWriters_ > 0)
- return writerLock_;
- else
- return null;
- }
這里要注意的是endRead返回的是writerLock,這樣他就可以完成notify和它互斥的writer
下面看一下WriterLock的實現(xiàn),同樣也是acquire和release方法
先看acquire
Java代碼
- public void acquire() throws InterruptedException {
- if (Thread.interrupted())
- throw new InterruptedException();
- InterruptedException ie = null;
- synchronized (this) {
- //試圖獲得writer權
- if (!startWriteFromNewWriter()) {
- for (;;) {
- try {
- WriterLock.this.wait();
- /**
- * 重新判斷是否能獲得控制權
- * 這時如果是writerLock的notify的話,理論上只有一個reader或者writer能夠結束等待
- * 如果是readerLock的notify的話,因為調用的是notifyAll,所以就必須重新競爭控制權
- */
- if (startWriteFromWaitingWriter())
- return;
- } catch (InterruptedException ex) {
- cancelledWaitingWriter();
- WriterLock.this.notify();
- ie = ex;
- break;
- }
- }
- }
- }
- if (ie != null) {
- // Fall through outside synch on interrupt.
- // On exception, we may need to signal readers.
- // It is not worth checking here whether it is strictly
- // necessary.
- readerLock_.signalWaiters();
- throw ie;
- }
- }
再看acquire調用的startWriteFromNewWriter,startWriteFromWaitingWriter和startWrite
Java代碼
- protected synchronized boolean startWriteFromNewWriter() {
- boolean pass = startWrite();
- //如果不能獲得控制權,則等待數(shù)+1
- if (!pass)
- ++waitingWriters_;
- return pass;
- }
- protected synchronized boolean startWrite() {
- // The allowWrite expression cannot be modified without
- // also changing startWrite, so is hard-wired
- //是否能獲得寫控制取決與是否已經(jīng)有人有控制權(包括讀和寫)
- boolean allowWrite = (activeWriter_ == null && activeReaders_ == 0);
- if (allowWrite)
- activeWriter_ = Thread.currentThread();
- return allowWrite;
- }
- protected synchronized boolean startWriteFromWaitingWriter() {
- boolean pass = startWrite();
- //如果能獲得控制權,則等待數(shù)-1
- if (pass)
- --waitingWriters_;
- return pass;
- }
再來看看WriterLock的release實現(xiàn)
Java代碼
- public void release() {
- Signaller s = endWrite();
- if (s != null)
- //如果沒有write的waiter,返回的的是readerLock_,則通知所有waiting的reader結束等待
- //如果有write的waiter,返回的的是writeLock,則通知一個正在等待的writer獲得控制權
- s.signalWaiters();
- }
- protected synchronized Signaller endWrite() {
- activeWriter_ = null;
- //如果沒有writer,則通知所有的等待的reader
- if (waitingReaders_ > 0 && allowReader())
- return readerLock_;
- //優(yōu)先通知一個等待的writer
- else if (waitingWriters_ > 0)
- return writerLock_;
- else
- return null;
- }
***看看WriterLock和ReaderLock如何實現(xiàn)Writer優(yōu)先
* 首先在競爭控制權時,Reader在競爭控制權必須確認既沒有占用控制權的Writer也沒有等待控制權的writer
這里是調用的是allowRead方法
activeWriter_ == null && waitingWriters_ == 0;
* 其次在WriterLock的release方法里,調用endWrite,然后也會調用allowReader,確認沒有等待的Writer才會返回readerLock_,并在signalWaiters里調用notifyAll通知所有的等待reader結束等待。而一旦有writer等待,則調用writerLock_,只通知等待的writer競爭控制權。具體代碼見上文。
* 同時在ReaderLock的release方法里,調用endRead,返回writerLock_,通知等待的writer競爭控制權。具體代碼見上文。
到此為止WriterPreferenceReadWriteLock的實現(xiàn)基本說完,這個版本的實現(xiàn)下載見http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html
#p#
1.3 EDU.oswego.cs.dl.util.concurrent.ReentrantWriterPreferenceReadWriteLock
上一篇說到了WriterPreferenceReadWriteLock,這一篇說一下ReentrantWriterPreferenceReadWriteLock。
顧名思義,該類實現(xiàn)的是可重入的的WriterPreferenceReadWriteLock。允許readers和writers在各自線程里反復獲得讀或寫的鎖。這種方法主要用在
該類是WriterPreferenceReadWriteLock的子類,與父類大體流程一致,但是startWrite,startRead ,allowReader ,endRead和endWrite被重寫,下面逐一解釋。
先看看新添加的成員變量
Java代碼
- /**在activeWriter線程里,有多少次write鎖的控制權被獲取**/
- protected long writeHolds_ = 0;
- /**存放的每個reader線程共申請獲得多少次讀控制,key值是thread對象,value是次數(shù)**/
- protected HashMap readers_ = new HashMap();
- /*一個int的常量緩存*/
- protected static final Integer IONE = new Integer(1);
再來看看Reader部分的重寫函數(shù)
Java代碼
- protected boolean allowReader() {
- //與父類的變化在于,activeWriter_ == Thread.currentThread(),
- //也就是說當本線程已經(jīng)獲得寫控制的時候,返回true
- return (activeWriter_ == null && waitingWriters_ == 0) ||
- activeWriter_ == Thread.currentThread();
- }
- protected synchronized boolean startRead() {
- Thread t = Thread.currentThread();
- //查看本線程是否已經(jīng)獲得讀控制
- Object c = readers_.get(t);
- if (c != null) { // already held -- just increment hold count
- //計數(shù)+1
- readers_.put(t, new Integer(((Integer)(c)).intValue()+1));
- ++activeReaders_;
- return true;
- }
- //調用allowReader
- else if (allowReader()) {
- //將本線程獲鎖次數(shù)紀錄在readers_這個map里
- readers_.put(t, IONE);
- ++activeReaders_;
- return true;
- }
- else
- return false;
- }
- protected synchronized Signaller endRead() {
- Thread t = Thread.currentThread();
- Object c = readers_.get(t);
- if (c == null)
- throw new IllegalStateException();
- --activeReaders_;
- if (c != IONE) { // 多于一個讀線程有控制權,more than one hold; 計數(shù)遞減
- int h = ((Integer)(c)).intValue()-1;
- Integer ih = (h == 1)? IONE : new Integer(h);
- readers_.put(t, ih);
- return null;
- }
- else {
- readers_.remove(t);
- if (writeHolds_ > 0) // 本線程還有寫鎖在占用控制權
- return null;
- //其余與父類實現(xiàn)一樣
- else if (activeReaders_ == 0 && waitingWriters_ > 0)
- return writerLock_;
- else
- return null;
- }
- }
Reader部分相對于父類的變化在于
* 每個reader試圖競爭控制權時,都會將本線程句柄與activeWriter進行比較,相同則認為是可以重入。
* 每個reader線程都在readers_的map里有一個計數(shù)器,判斷當前有多少次獲得reader鎖權,釋放的時候,只有當計數(shù)器為0時才通知其他寫線程結束wait。
接著看看Writer部分的重寫函數(shù)
Java代碼
- protected synchronized boolean startWrite() {
- if (activeWriter_ == Thread.currentThread()) { // 反復重入
- ++writeHolds_;
- return true;
- }
- else if (writeHolds_ == 0) {
- //如果沒有人在讀(activeReaders_==0)和,或者在讀的線程和本線程一樣,并且readers里沒有其他線程
- if (activeReaders_ == 0 ||
- (readers_.size() == 1 &&
- readers_.get(Thread.currentThread()) != null)) {
- //如果本線程在讀中,則也可以進入writeLock
- activeWriter_ = Thread.currentThread();
- writeHolds_ = 1;
- return true;
- }
- else
- return false;
- }
- else
- return false;
- }
- protected synchronized Signaller endWrite() {
- --writeHolds_;
- if (writeHolds_ > 0)
- //這是主要與父類不一樣的地方,寫鎖計數(shù)標示仍然有寫鎖沒有被釋放
- return null;
- else {
- //與父類一致
- activeWriter_ = null;
- if (waitingReaders_ > 0 && allowReader())
- return readerLock_;
- else if (waitingWriters_ > 0)
- return writerLock_;
- else
- return null;
- }
- }
Writer主要與父類不同的地方是
* 通過writeHolds來記錄本線程獲得鎖的次數(shù),以便在release時正確調用通知機制
* 通過當前線程和activeWriter線程比較來實現(xiàn)重入
1.3 EDU.oswego.cs.dl.util.concurrent.ReaderPreferenceReadWriteLock
ReaderPreferenceReadWriteLock類和父類唯一不同就是allowReader方法
Java代碼
- protected boolean allowReader() {
- //只要沒有寫,就允許讀
- return activeWriter_ == null;
- }
這個類在大多數(shù)場景下都不太實用,多數(shù)人主要還是使用writer優(yōu)先鎖。
【編輯推薦】