链接:http://tutorials.jenkov.com/java-concurrency/read-write-locks.html
以下对链接中的代码加了一些注释,对canGrantReadAccess函数添加了注释。
java.util.concurrent包中提供了读写锁的实现。使用读写锁,同一时间中多个线程可以同时读同一资源,但是如果一个线程在写资源时,其他读和写线程都不能使用资源。虽说java api已经提供了实现,但是知道如何实现的还是很有用的。
读写锁的非可重入实现
先总结一下读写访问的条件:
读访问:如果没有线程写时,也没有写请求的线程,可以获得读访问
写访问:如果没有线程写和读。
如果读的线程很多时,写的线程可能会因为等待所有读线程而饿死。因此要使用增加请求变量,将读写锁设计成写优先读写锁。
public class ReadWriteLock{
private int readers = 0;
private int writers = 0;
private int writeRequests = 0;
public synchronized void lockRead() throws InterruptedException{
while(writers > 0 || writeRequests > 0){//有写请求时,后续读者就不在获得锁,已获得锁的读者继续执行,因此实现了写优先的规则
wait();
}
readers++;
}
public synchronized void unlockRead(){
readers--;
notifyAll();
}
public synchronized void lockWrite() throws InterruptedException{
writeRequests++;
while(readers > 0 || writers > 0){
wait();
}
writeRequests--;
writers++;
}
public synchronized void unlockWrite() throws InterruptedException{
writers--;
notifyAll();
}
}
note:注意使用notifyAll而不是notify,参考链接中有详细讲解。
读写锁的可重入实现
上述代码是不可重入的,不可重入的代码很容易造成死锁。比如下面的例子:
- 线程1得到写访问
- 线程2请求写访问,由于有了读者,因此被阻塞
- 线程1重新请求读访问,但是有线程请求写访问,因此被阻塞
这种情况下就产生了死锁。因此实现可重入很必要,下面分别为每一部分实现可重入。
Read Reentrance
如果没有写者和读者,线程可以再次获得读访问,或者它已经获得了读访问。
扫描二维码关注公众号,回复:
2940215 查看本文章
public class ReadWriteLock{
private Map<Thread, Integer> readingThreads =
new HashMap<Thread, Integer>();
private int writers = 0;
private int writeRequests = 0;
public synchronized void lockRead() throws InterruptedException{
Thread callingThread = Thread.currentThread();
while(! canGrantReadAccess(callingThread)){
wait();
}
readingThreads.put(callingThread,
(getAccessCount(callingThread) + 1));
}
public synchronized void unlockRead(){
Thread callingThread = Thread.currentThread();
int accessCount = getAccessCount(callingThread);
if(accessCount == 1){ readingThreads.remove(callingThread); }
else { readingThreads.put(callingThread, (accessCount -1)); }
notifyAll();
}
private boolean canGrantReadAccess(Thread callingThread){
if(writers > 0) return false;
if(isReader(callingThread) return true;//当无写者使用资源,且当前线程为已获得访问权限的线程,则能够再次访问。
if(writeRequests > 0) return false;
return true;//无写者占用,该线程之前也未获得访问权限,也没有写者请求访问。
}
private int getReadAccessCount(Thread callingThread){
Integer accessCount = readingThreads.get(callingThread);
if(accessCount == null) return 0;
return accessCount.intValue();
}
private boolean isReader(Thread callingThread){
return readingThreads.get(callingThread) != null;
}
}
Write Reentrance
写重入被授予仅当线程已经获得了写访问。
public class ReadWriteLock{
private Map<Thread, Integer> readingThreads =
new HashMap<Thread, Integer>();
private int writeAccesses = 0;
private int writeRequests = 0;
private Thread writingThread = null;
public synchronized void lockWrite() throws InterruptedException{
writeRequests++;
Thread callingThread = Thread.currentThread();
while(! canGrantWriteAccess(callingThread)){
wait();
}
writeRequests--;
writeAccesses++;
writingThread = callingThread;
}
public synchronized void unlockWrite() throws InterruptedException{
writeAccesses--;
if(writeAccesses == 0){
writingThread = null;
}
notifyAll();
}
private boolean canGrantWriteAccess(Thread callingThread){
if(hasReaders()) return false;
if(writingThread == null) return true;//没有读者访问,也没有写者访问
if(!isWriter(callingThread)) return false;
return true;//没有读者访问,但是有写者访问,该线程就是拥有访问权限的写者,返回true
}
private boolean hasReaders(){
return readingThreads.size() > 0;
}
private boolean isWriter(Thread callingThread){
return writingThread == callingThread;
}
}
Read to Write Reentrance
获得读访问的线程也可以获得写访问,仅当该线程是唯一的读线程。
public class ReadWriteLock{
private Map<Thread, Integer> readingThreads =
new HashMap<Thread, Integer>();
private int writeAccesses = 0;
private int writeRequests = 0;
private Thread writingThread = null;
public synchronized void lockWrite() throws InterruptedException{
writeRequests++;
Thread callingThread = Thread.currentThread();
while(! canGrantWriteAccess(callingThread)){
wait();
}
writeRequests--;
writeAccesses++;
writingThread = callingThread;
}
public synchronized void unlockWrite() throws InterruptedException{
writeAccesses--;
if(writeAccesses == 0){
writingThread = null;
}
notifyAll();
}
private boolean canGrantWriteAccess(Thread callingThread){
if(isOnlyReader(callingThread)) return true;//当仅有一个读者线程,且为该线程
if(hasReaders()) return false;
if(writingThread == null) return true;//当没有读者和写者线程
if(!isWriter(callingThread)) return false;
return true;//当为获得当前写权限的写者线程
}
private boolean hasReaders(){
return readingThreads.size() > 0;
}
private boolean isWriter(Thread callingThread){
return writingThread == callingThread;
}
private boolean isOnlyReader(Thread thread){
return readingThreads.size() == 1 &&readingThreads.get(callingThread) != null;
}
}
Write to Read Reentrance
一个获得了写访问的线程总是可以获得读访问的。
public class ReadWriteLock{
...
private boolean canGrantReadAccess(Thread callingThread){
if(isWriter(callingThread)) return true;//如果该线程是拥有写访问锁的线程,可以获得读锁
if(writingThread != null) return false;
if(isReader(callingThread) return true;
if(writeRequests > 0) return false;
return true;
}
...
}
Fully Reentrant ReadWriteLock
这是完整的程序。
public class ReadWriteLock{
private Map<Thread, Integer> readingThreads =
new HashMap<Thread, Integer>();
private int writeAccesses = 0;
private int writeRequests = 0;
private Thread writingThread = null;
public synchronized void lockRead() throws InterruptedException{
Thread callingThread = Thread.currentThread();
while(! canGrantReadAccess(callingThread)){
wait();
}
readingThreads.put(callingThread,
(getReadAccessCount(callingThread) + 1));
}
private boolean canGrantReadAccess(Thread callingThread){
if( isWriter(callingThread) ) return true;
if( hasWriter() ) return false;
if( isReader(callingThread) ) return true;
if( hasWriteRequests() ) return false;
return true;
}
public synchronized void unlockRead(){
Thread callingThread = Thread.currentThread();
if(!isReader(callingThread)){
throw new IllegalMonitorStateException("Calling Thread does not" +
" hold a read lock on this ReadWriteLock");
}
int accessCount = getReadAccessCount(callingThread);
if(accessCount == 1){ readingThreads.remove(callingThread); }
else { readingThreads.put(callingThread, (accessCount -1)); }
notifyAll();
}
public synchronized void lockWrite() throws InterruptedException{
writeRequests++;
Thread callingThread = Thread.currentThread();
while(! canGrantWriteAccess(callingThread)){
wait();
}
writeRequests--;
writeAccesses++;
writingThread = callingThread;
}
public synchronized void unlockWrite() throws InterruptedException{
if(!isWriter(Thread.currentThread()){
throw new IllegalMonitorStateException("Calling Thread does not" +
" hold the write lock on this ReadWriteLock");
}
writeAccesses--;
if(writeAccesses == 0){
writingThread = null;
}
notifyAll();
}
private boolean canGrantWriteAccess(Thread callingThread){
if(isOnlyReader(callingThread)) return true;
if(hasReaders()) return false;
if(writingThread == null) return true;
if(!isWriter(callingThread)) return false;
return true;
}
private int getReadAccessCount(Thread callingThread){
Integer accessCount = readingThreads.get(callingThread);
if(accessCount == null) return 0;
return accessCount.intValue();
}
private boolean hasReaders(){
return readingThreads.size() > 0;
}
private boolean isReader(Thread callingThread){
return readingThreads.get(callingThread) != null;
}
private boolean isOnlyReader(Thread callingThread){
return readingThreads.size() == 1 &&
readingThreads.get(callingThread) != null;
}
private boolean hasWriter(){
return writingThread != null;
}
private boolean isWriter(Thread callingThread){
return writingThread == callingThread;
}
private boolean hasWriteRequests(){
return this.writeRequests > 0;
}
}