自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java的ReadWriteLock實現(xiàn)機制解析

開發(fā) 后端
如果接觸過多線程編程或者大規(guī)模并發(fā)應用的開發(fā)的人都應該知道Readers-writer lock的設計模式,從英文字面上看就是對于資源允許多個Reader(復數(shù))并發(fā)讀,單個Writer寫的鎖機制,而Reader和Writer互斥。

如果接觸過多線程編程或者大規(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代碼

 

 

  1. public interface ReadWriteLock {     
  2.   /** get the readLock **/    
  3.   Sync readLock();     
  4.     
  5.   /** get the writeLock **/    
  6.   Sync writeLock();     
  7. }    

 

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代碼

 

 

  1. /*用來給Writer判斷是否可以拿到write控制權*/    
  2. protected long activeReaders_ = 0;     
  3. /*當前writer線程*/    
  4. protected Thread activeWriter_ = null;     
  5. /*可以用來作為策略調整,此版本中沒有太多作用*/    
  6. protected long waitingReaders_ = 0;     
  7. /*等待中的寫,用來給Reader判斷是否還有Writer在等待,以便實現(xiàn)Writer優(yōu)先*/    
  8. protected long waitingWriters_ = 0;     
  9.     
  10. /*實際的ReaderLock*/    
  11. protected final ReaderLock readerLock_ = new ReaderLock();     
  12. /*實際的WriterLock*/    
  13. protected final WriterLock writerLock_ = new WriterLock();    

 

先來看看 ReaderLock,它的兩個主要方法acquire和release

其中acquire的代碼如下

Java代碼

 

 

  1. public void acquire() throws InterruptedException {     
  2.     if (Thread.interrupted())     
  3.         throw new InterruptedException();     
  4.     InterruptedException ie = null;     
  5.     synchronized (this) {     
  6.         /**    
  7.          * 判斷是否能夠獲得讀權    
  8.          */    
  9.         if (!startReadFromNewReader()) {     
  10.             for (;;) {     
  11.                 try {     
  12.                     /**    
  13.                      * 等待notify    
  14.                      */    
  15.                     ReaderLock.this.wait();     
  16.                     /**    
  17.                      * 再次判斷能否獲得讀權    
  18.                      * 因為此處是Writer優(yōu)先,當一個writer釋放時,    
  19.                      * reader還必須等待其他wait的writer獲得控制權并釋放后才能獲得控制權    
  20.                      */    
  21.                     if (startReadFromWaitingReader())     
  22.                         return;     
  23.                 } catch (InterruptedException ex) {     
  24.                     cancelledWaitingReader();     
  25.                     ie = ex;     
  26.                     break;     
  27.                 }     
  28.             }     
  29.         }     
  30.     }     
  31.     if (ie != null) {     
  32.         // fall through outside synch on interrupt.     
  33.         // This notification is not really needed here,     
  34.         // but may be in plausible subclasses     
  35.         writerLock_.signalWaiters();     
  36.         throw ie;     
  37.     }     
  38. }   

 

acquire調用startReadFromNewReader,startReadFromWaitingReader,以及allowReader方法,這三個方法都屬于WriterPreferenceReadWriteLock類

Java代碼

 

 

  1. protected synchronized boolean startReadFromNewReader() {     
  2.     boolean pass = startRead();     
  3.     if (!pass)     
  4.         ++waitingReaders_;     
  5.     return pass;     
  6. }     
  7.     
  8.     
  9. protected synchronized boolean startReadFromWaitingReader() {     
  10.     boolean pass = startRead();     
  11.     if (pass)     
  12.         --waitingReaders_;     
  13.     return pass;     
  14. }     
  15.     
  16.     
  17.     
  18. protected boolean allowReader() {     
  19.     //通過是否有正有控制權的activeWriter_和等待中的waitingWriters_來判斷是Reader是否能獲得控制權     
  20.     return activeWriter_ == null && waitingWriters_ == 0;     
  21. }     
  22.     
  23.     
  24. protected synchronized boolean startRead() {     
  25.     boolean allowRead = allowReader();     
  26.     if (allowRead)     
  27.         ++activeReaders_;     
  28.     return allowRead;     
  29. }    

 

另外release的代碼如下

Java代碼

 

 

  1. public void release() {     
  2.     Signaller s = endRead();     
  3.     if (s != null)     
  4.         s.signalWaiters();     
  5. }     
  6.     
  7.     
  8. protected synchronized Signaller endRead() {     
  9.     //只有當沒有Reader在控制以及有等待的Writer的時候才返回     
  10.     //因為Reader之間沒有互斥,所以返回writerLock     
  11.     if (--activeReaders_ == 0 && waitingWriters_ > 0)     
  12.         return writerLock_;     
  13.     else    
  14.         return null;     
  15. }    

 

這里要注意的是endRead返回的是writerLock,這樣他就可以完成notify和它互斥的writer

下面看一下WriterLock的實現(xiàn),同樣也是acquire和release方法

先看acquire

Java代碼

 

 

  1. public void acquire() throws InterruptedException {     
  2.     if (Thread.interrupted())     
  3.         throw new InterruptedException();     
  4.     InterruptedException ie = null;     
  5.     synchronized (this) {     
  6.         //試圖獲得writer權     
  7.         if (!startWriteFromNewWriter()) {     
  8.             for (;;) {     
  9.                 try {     
  10.     
  11.                     WriterLock.this.wait();      
  12.                     /**    
  13.                      * 重新判斷是否能獲得控制權    
  14.                      * 這時如果是writerLock的notify的話,理論上只有一個reader或者writer能夠結束等待    
  15.                      * 如果是readerLock的notify的話,因為調用的是notifyAll,所以就必須重新競爭控制權    
  16.                      */    
  17.                     if (startWriteFromWaitingWriter())     
  18.                         return;     
  19.                 } catch (InterruptedException ex) {     
  20.                     cancelledWaitingWriter();     
  21.                     WriterLock.this.notify();     
  22.                     ie = ex;     
  23.                     break;     
  24.                 }     
  25.             }     
  26.         }     
  27.     }     
  28.     if (ie != null) {     
  29.         // Fall through outside synch on interrupt.     
  30.         // On exception, we may need to signal readers.     
  31.         // It is not worth checking here whether it is strictly     
  32.         // necessary.     
  33.         readerLock_.signalWaiters();     
  34.         throw ie;     
  35.     }     
  36. }  

 

再看acquire調用的startWriteFromNewWriter,startWriteFromWaitingWriter和startWrite

Java代碼

 

 

  1. protected synchronized boolean startWriteFromNewWriter() {     
  2.     boolean pass = startWrite();     
  3.     //如果不能獲得控制權,則等待數(shù)+1     
  4.     if (!pass)     
  5.         ++waitingWriters_;     
  6.     return pass;     
  7. }     
  8.     
  9.     
  10. protected synchronized boolean startWrite() {     
  11.     
  12.     // The allowWrite expression cannot be modified without     
  13.     // also changing startWrite, so is hard-wired     
  14.     //是否能獲得寫控制取決與是否已經(jīng)有人有控制權(包括讀和寫)     
  15.     boolean allowWrite = (activeWriter_ == null && activeReaders_ == 0);     
  16.     if (allowWrite)     
  17.         activeWriter_ = Thread.currentThread();     
  18.     return allowWrite;     
  19. }     
  20.     
  21.     
  22. protected synchronized boolean startWriteFromWaitingWriter() {     
  23.     boolean pass = startWrite();     
  24.     //如果能獲得控制權,則等待數(shù)-1     
  25.     if (pass)     
  26.         --waitingWriters_;     
  27.     return pass;     
  28. }   

 

再來看看WriterLock的release實現(xiàn)

Java代碼

 

 

  1. public void release() {     
  2.     Signaller s = endWrite();     
  3.     if (s != null)     
  4.         //如果沒有write的waiter,返回的的是readerLock_,則通知所有waiting的reader結束等待     
  5.         //如果有write的waiter,返回的的是writeLock,則通知一個正在等待的writer獲得控制權     
  6.         s.signalWaiters();     
  7. }     
  8.     
  9.     
  10. protected synchronized Signaller endWrite() {     
  11.     activeWriter_ = null;     
  12.     //如果沒有writer,則通知所有的等待的reader     
  13.     if (waitingReaders_ > 0 && allowReader())     
  14.         return readerLock_;     
  15.     //優(yōu)先通知一個等待的writer     
  16.     else if (waitingWriters_ > 0)     
  17.         return writerLock_;     
  18.     else    
  19.         return null;     
  20. }  

 

***看看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代碼

 

 

  1. /**在activeWriter線程里,有多少次write鎖的控制權被獲取**/         
  2. protected long writeHolds_ = 0;       
  3.     
  4.  /**存放的每個reader線程共申請獲得多少次讀控制,key值是thread對象,value是次數(shù)**/    
  5. protected HashMap readers_ = new HashMap();     
  6.     
  7. /*一個int的常量緩存*/    
  8. protected static final Integer IONE = new Integer(1);    

 

再來看看Reader部分的重寫函數(shù)

Java代碼

 

 

  1. protected boolean allowReader() {     
  2.     //與父類的變化在于,activeWriter_ == Thread.currentThread(),     
  3.     //也就是說當本線程已經(jīng)獲得寫控制的時候,返回true     
  4.     return (activeWriter_ == null && waitingWriters_ == 0) ||     
  5.         activeWriter_ == Thread.currentThread();     
  6. }     
  7.     
  8. protected synchronized boolean startRead() {     
  9.     Thread t = Thread.currentThread();     
  10.     //查看本線程是否已經(jīng)獲得讀控制     
  11.     Object c = readers_.get(t);     
  12.     if (c != null) { // already held -- just increment hold count     
  13.         //計數(shù)+1     
  14.         readers_.put(t, new Integer(((Integer)(c)).intValue()+1));     
  15.         ++activeReaders_;     
  16.         return true;     
  17.     }     
  18.     //調用allowReader     
  19.     else if (allowReader()) {     
  20.         //將本線程獲鎖次數(shù)紀錄在readers_這個map里     
  21.         readers_.put(t, IONE);     
  22.         ++activeReaders_;     
  23.         return true;     
  24.     }     
  25.     else    
  26.         return false;     
  27. }     
  28.     
  29.     
  30. protected synchronized Signaller endRead() {     
  31.     Thread t = Thread.currentThread();     
  32.     Object c = readers_.get(t);     
  33.     if (c == null)     
  34.         throw new IllegalStateException();     
  35.     --activeReaders_;     
  36.     if (c != IONE) { // 多于一個讀線程有控制權,more than one hold; 計數(shù)遞減     
  37.         int h = ((Integer)(c)).intValue()-1;     
  38.         Integer ih = (h == 1)? IONE : new Integer(h);     
  39.         readers_.put(t, ih);     
  40.         return null;     
  41.     }     
  42.     else {     
  43.         readers_.remove(t);     
  44.     
  45.         if (writeHolds_ > 0// 本線程還有寫鎖在占用控制權     
  46.             return null;     
  47.         //其余與父類實現(xiàn)一樣     
  48.         else if (activeReaders_ == 0 && waitingWriters_ > 0)     
  49.             return writerLock_;     
  50.         else    
  51.             return null;     
  52.     }     
  53. }  

 

Reader部分相對于父類的變化在于

* 每個reader試圖競爭控制權時,都會將本線程句柄與activeWriter進行比較,相同則認為是可以重入。

* 每個reader線程都在readers_的map里有一個計數(shù)器,判斷當前有多少次獲得reader鎖權,釋放的時候,只有當計數(shù)器為0時才通知其他寫線程結束wait。

接著看看Writer部分的重寫函數(shù)

Java代碼

 

 

  1. protected synchronized boolean startWrite() {     
  2.     if (activeWriter_ == Thread.currentThread()) { // 反復重入           
  3.         ++writeHolds_;     
  4.         return true;     
  5.     }     
  6.     else if (writeHolds_ == 0) {     
  7.         //如果沒有人在讀(activeReaders_==0)和,或者在讀的線程和本線程一樣,并且readers里沒有其他線程     
  8.         if (activeReaders_ == 0 ||      
  9.                 (readers_.size() == 1 &&      
  10.                  readers_.get(Thread.currentThread()) != null)) {     
  11.             //如果本線程在讀中,則也可以進入writeLock     
  12.             activeWriter_ = Thread.currentThread();     
  13.             writeHolds_ = 1;     
  14.             return true;     
  15.         }     
  16.         else    
  17.             return false;     
  18.     }     
  19.     else    
  20.         return false;     
  21. }     
  22.     
  23.     
  24. protected synchronized Signaller endWrite() {     
  25.     --writeHolds_;     
  26.     if (writeHolds_ > 0)       
  27.         //這是主要與父類不一樣的地方,寫鎖計數(shù)標示仍然有寫鎖沒有被釋放     
  28.         return null;     
  29.     else {     
  30.         //與父類一致          
  31.         activeWriter_ = null;     
  32.         if (waitingReaders_ > 0 && allowReader())     
  33.             return readerLock_;     
  34.         else if (waitingWriters_ > 0)     
  35.             return writerLock_;     
  36.         else    
  37.             return null;     
  38.     }     
  39. }  

 

Writer主要與父類不同的地方是

* 通過writeHolds來記錄本線程獲得鎖的次數(shù),以便在release時正確調用通知機制

* 通過當前線程和activeWriter線程比較來實現(xiàn)重入

1.3 EDU.oswego.cs.dl.util.concurrent.ReaderPreferenceReadWriteLock

ReaderPreferenceReadWriteLock類和父類唯一不同就是allowReader方法

Java代碼

 

 

  1. protected boolean allowReader() {     
  2.     //只要沒有寫,就允許讀     
  3.     return activeWriter_ == null;     
  4. }   

 

這個類在大多數(shù)場景下都不太實用,多數(shù)人主要還是使用writer優(yōu)先鎖。

【編輯推薦】

  1. 深入Java核心 Java內存分配原理精講
  2. 初學Java語言之多態(tài)初體驗
  3. Java中用接口實現(xiàn)多繼承和多態(tài)的方法
  4. 淺談.NET中不一樣的多態(tài)
責任編輯:金賀 來源: JavaEye博客
相關推薦

2011-11-23 09:39:33

JavaClassLOader機制

2010-10-13 10:24:38

垃圾回收機制JVMJava

2015-09-28 15:59:00

Java動態(tài)代理機制

2013-08-02 14:19:50

Java日志緩存

2017-08-17 15:13:52

PostgreSQL MVCC機制

2025-01-24 08:19:57

2010-09-17 13:02:11

JAVA反射機制

2015-10-26 09:25:42

2019-05-27 14:40:43

Java同步機制多線程編程

2017-09-05 10:20:30

PyTorchTensorPython

2010-04-26 10:44:27

Oracle SCN

2010-01-25 18:24:11

C++

2024-12-18 21:37:24

2010-09-14 09:30:04

Java多態(tài)

2023-10-20 09:51:00

編程開發(fā)

2011-07-01 15:04:49

Qt 內省

2011-12-12 10:33:47

JavaNIO

2011-12-12 10:19:00

JavaNIO

2011-12-15 09:33:19

Java

2014-11-04 10:34:27

JavaCache
點贊
收藏

51CTO技術棧公眾號