程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java的ReadWriteLock實現機制解析(1)

Java的ReadWriteLock實現機制解析(1)

編輯:關於JAVA

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

現在的JDK裡有一個ReadWriteLock的接口和一個ReentrantReadWriteLock的實現類,而其作者是赫赫有名的Doug Lea大牛(他有本 Concurrent Programming in Java Design Principles and Pattern ,推薦一下) 。早在JDK 1.4的時代,他就發表了自己的cocurrent包實現,其中就有多個ReadWriteLock的實現。下面會先聊一下早期Doug Lea在EDU.oswego.cs.dl.util.concurrent版本中對ReadWriteLock的實現,最後再說JDK版本的。

1.EDU.oswego.cs.dl.util.concurrent的實現

Doug Lea的這個版本:EDU.oswego.cs.dl.util.concurrent包中ReadWriteLock所包含的內容比JDK要豐富不少,除了ReentrantReadWriteLock還有若干個其他實現。先看一下ReadWriteLock在其中的類繼承關系。源代碼下載

這其中包含了4個ReadWriteLock,包括先進先出的FIFOReadWriteLock ,Writer優先的WriterPreferenceReadWriteLock ,Reader優先的ReaderPreferenceReadWriteLock ,可重入ReentrantWriterPreferenceReadWriteLock 。

1.1 EDU.oswego.cs.dl.util.concurrent.ReadWriteLock接口

Java代碼

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

ReadWriteLock的接口定義和後來JDK的實現基本一致。readLock和writeLock都實現Sync接口,這個接口兩個主要的方法是acquire和realse,在以後的JDK中,變成了Lock的lock方法和unlock方法。

1.2 WriterPreferenceReadWriteLock類

這個類包含了WriterLock類型的writerLock_和ReaderLock類型的readerLock_兩個成員,而這兩個類型又分別為WriterPreferenceReadWriteLock的內部類,這樣做的一個考慮可能是為了能夠讓這兩個類能夠訪問WriterPreferenceReadWriteLock的成員以及互相訪問。

先看看WriterPreferenceReadWriteLock中ReaderLock的實現

該類的幾個成員以及解釋如下

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在等待,以便實現Writer優先*/
  8. protected long waitingWriters_ = 0;
  9. /*實際的ReaderLock*/
  10. protected final ReaderLock readerLock_ = new ReaderLock();
  11. /*實際的WriterLock*/
  12. 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優先,當一個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. protected synchronized boolean startReadFromWaitingReader() {
  8. boolean pass = startRead();
  9. if (pass)
  10. --waitingReaders_;
  11. return pass;
  12. }
  13. protected boolean allowReader() {
  14. //通過是否有正有控制權的activeWriter_和等待中的waitingWriters_來判斷是Reader是否能獲得控制權
  15. return activeWriter_ == null && waitingWriters_ == 0;
  16. }
  17. protected synchronized boolean startRead() {
  18. boolean allowRead = allowReader();
  19. if (allowRead)
  20. ++activeReaders_;
  21. return allowRead;
  22. }

另外release的代碼如下

Java代碼

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

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

下面看一下WriterLock的實現,同樣也是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. WriterLock.this.wait();
  11. /**
  12. * 重新判斷是否能獲得控制權
  13. * 這時如果是writerLock的notify的話,理論上只有一個reader或者writer能夠結束等待
  14. * 如果是readerLock的notify的話,因為調用的是notifyAll,所以就必須重新競爭控制權
  15. */
  16. if (startWriteFromWaitingWriter())
  17. return;
  18. } catch (InterruptedException ex) {
  19. cancelledWaitingWriter();
  20. WriterLock.this.notify();
  21. IE = ex;
  22. break;
  23. }
  24. }
  25. }
  26. }
  27. if (IE != null) {
  28. // Fall through outside synch on interrupt.
  29. // On exception, we may need to signal readers.
  30. // It is not worth checking here whether it is strictly
  31. // necessary.
  32. readerLock_.signalWaiters();
  33. throw IE;
  34. }
  35. }

再看acquire調用的startWriteFromNewWriter,startWriteFromWaitingWriter和startWrite

Java代碼

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

再來看看WriterLock的release實現

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. protected synchronized Signaller endWrite() {
  9. activeWriter_ = null;
  10. //如果沒有writer,則通知所有的等待的reader
  11. if (waitingReaders_ > 0 && allowReader())
  12. return readerLock_;
  13. //優先通知一個等待的writer
  14. else if (waitingWriters_ > 0)
  15. return writerLock_;
  16. else
  17. return null;
  18. }

最後看看WriterLock和ReaderLock如何實現Writer優先

* 首先在競爭控制權時,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的實現基本說完,這個版本的實現下載見http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.Html

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved