前段时间看了一系列并发编程博客,感觉写的不错。这里记录一下其中可重入读写锁的自己实现方法,虽然java中都有封装好的读写锁可用,但分析一下代码有助于理解锁机制的原理。
博客原地址
package cn.wcl.readWriteLock;
import java.util.HashMap;
import java.util.Map;
/**
* @author www
* 可重入读写锁demo
* 【只是一种模拟,用于理解可重入读写锁的原理,java中有已经封装好的锁可以使用,无需自己设计锁】
* 基于情况:写操作比读操作优先级高,读操作比写操作频繁得多。
* 同一时间可以有多个线程对资源进行读取,但获取写锁的线程是唯一的
* 获取读锁的条件:没有线程正在做写操作,且没有线程在请求写操作
* 获取写锁的条件:没有线程正在做读写操作
* @time:2017年4月26日 下午5:06:46
*/
public class ReadWriteLock {
/**
* 拥有读锁的Thread:用一个map来存储已经持有读锁的线程以及对应线程获取读锁的次数(用于可重入锁统计重入次数)
*/
private Map<Thread, Integer> readingThreads = new HashMap<Thread, Integer>();
/**
* 写锁的个数
*/
private int writeAccesses = 0;
/**
* 申请写锁的线程数
*/
private int writeRequest = 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);
}
/**
* @author www
* 是否能获得读锁许可
* @param callingThread
* @return
*/
private boolean canGrantReadAccess(Thread callingThread) {
//注意条件的顺序,代表了它们的优先级
if(isWriter(callingThread)) return true;//如果当前线程在写,那么该线程也可以读
if(hasWriter()) return false;//有其他线程在写,那么当前线程不能读
if(isReader(callingThread)) return true;//当前线程已经获得读锁,可以重入
if(hasWriterRequests()) return false;//当前线程没有获得读锁,有其他线程请求写锁,则不能读,因为读优先级低
//没有写锁请求,可能有其他线程在读,也能没有,当前线程可以获得读锁,因为多个线程可以一起读
return true;
}
public synchronized void unlockRead() {
Thread callingThread = Thread.currentThread();
if(!isReader(callingThread)) {
throw new IllegalMonitorStateException("callingThread未持有读锁!");
}
int readAccessCount = getReadAccessCount(callingThread);
if(readAccessCount == 1) {
readingThreads.remove(callingThread);
} else {
//如果重入过,重入次数-1
readingThreads.put(callingThread, readAccessCount - 1);
}
//用notifyAll()!
//因为读操作可以所有线程一起进行,只notify()唤醒一个,会导致只有一个等待的读线程获得锁,效率低
notifyAll();
}
public synchronized void lockWrite() throws InterruptedException {
writeRequest ++;
Thread callingThread = Thread.currentThread();
while(!canGrantWriteAccess(callingThread)) {
wait();
}
writeAccesses ++;
writeRequest --;
writingThread = callingThread;
}
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 synchronized void unlockWrite() {
Thread callingThread = Thread.currentThread();
if(!isWriter(callingThread)) {
throw new IllegalMonitorStateException("callingThread未持有写锁!");
}
writeAccesses --;
/*writingThread = null;*/ //不能直接置为null,因为有可能是重入锁
if(writeAccesses == 0) {
writingThread = null;
}
notifyAll();
}
private boolean hasReaders() {
return !readingThreads.isEmpty();
}
private boolean isOnlyReader(Thread callingThread) {
return readingThreads.size() == 1 && readingThreads.containsKey(callingThread);
}
private boolean hasWriterRequests() {
return writeRequest > 0;
}
private boolean isReader(Thread callingThread) {
return readingThreads.containsKey(callingThread);
}
private boolean hasWriter() {
return writingThread != null;
}
private boolean isWriter(Thread callingThread) {
return writingThread == callingThread;
}
private int getReadAccessCount(Thread callingThread) {
Integer count = readingThreads.get(callingThread);
if(count == null) {
return 0;
}
return count.intValue();
}
}