原文地址: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在其中的类继承关系。源代码下载
![](https://i-blog.csdnimg.cn/blog_migrate/91d6ce8428aa81dd9a994c1bea65f2fc.jpeg)
这其中包含了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