Java的ReadWriteLock实现机制解析(一)

来源:互联网 发布:小猪cms收银系统 编辑:程序博客网 时间:2024/05/22 13:15

原文地址:http://developer.51cto.com/art/201103/249288.htm

如果接触过多线程编程或者大规模并发应用的开发的人都应该知道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代码

public interface ReadWriteLock {       /** get the readLock **/      Sync readLock();           /** get the writeLock **/      Sync writeLock();     }

ReadWriteLock的接口定义和后来JDK的实现基本一致。readLock和writeLock都实现Sync接口,这个接口两个主要的方法是acquire和realse,在以后的JDK中,变成了Lock的lock方法和unlock方法。

1.2 WriterPreferenceReadWriteLock类

这个类包含了WriterLock类型的writerLock_和ReaderLock类型的readerLock_两个成员,而这两个类型又分别为WriterPreferenceReadWriteLock的内部类,这样做的一个考虑可能是为了能够让这两个类能够访问WriterPreferenceReadWriteLock的成员以及互相访问。

先看看WriterPreferenceReadWriteLock中ReaderLock的实现

该类的几个成员以及解释如下

Java代码

/*用来给Writer判断是否可以拿到write控制权*/    protected long activeReaders_ = 0;     /*当前writer线程*/    protected Thread activeWriter_ = null;     /*可以用来作为策略调整,此版本中没有太多作用*/    protected long waitingReaders_ = 0;     /*等待中的写,用来给Reader判断是否还有Writer在等待,以便实现Writer优先*/    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优先,当一个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的实现,同样也是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();         //如果不能获得控制权,则等待数+1         if (!pass)             ++waitingWriters_;         return pass;     }             protected synchronized boolean startWrite() {             // The allowWrite expression cannot be modified without         // also changing startWrite, so is hard-wired         //是否能获得写控制取决与是否已经有人有控制权(包括读和写)         boolean allowWrite = (activeWriter_ == null && activeReaders_ == 0);         if (allowWrite)             activeWriter_ = Thread.currentThread();         return allowWrite;     }             protected synchronized boolean startWriteFromWaitingWriter() {         boolean pass = startWrite();         //如果能获得控制权,则等待数-1         if (pass)             --waitingWriters_;         return pass;     } 

再来看看WriterLock的release实现

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_;         //优先通知一个等待的writer         else if (waitingWriters_ > 0)             return writerLock_;         else            return null;     } 

最后看看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

Java的ReadWriteLock实现机制解析(二)

0 0
原创粉丝点击