基本使用
class RWDictionary {
private final Map<String, Data> m = new TreeMap<String, Data>();
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
public Data get(String key) {
r.lock();
try { return m.get(key); }
finally { r.unlock(); }
}
public String[] allKeys() {
r.lock();
try { return m.keySet().toArray(); }
finally { r.unlock(); }
}
public Data put(String key, Data value) {
w.lock();
try { return m.put(key, value); }
finally { w.unlock(); }
}
public void clear() {
w.lock();
try { m.clear(); }
finally { w.unlock(); }
}
}
成员变量和构造方法
/**
* 读锁
*/
private final ReentrantReadWriteLock.ReadLock readerLock;
/**
* 写锁
*/
private final ReentrantReadWriteLock.WriteLock writerLock;
/**
* 同步器
*/
final Sync sync;
/**
* 无参构造,默认初始化非公平锁
*/
public ReentrantReadWriteLock() {
this(false);
}
/**
* 根据fair判断初始化的锁是公平锁或非公平锁
* (公平锁和非公平锁对于读写锁有什么作用?)
*/
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this); //初始化读锁
writerLock = new WriteLock(this); //初始化写锁
}
public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }
内部类
Sync
abstract static class Sync extends AbstractQueuedSynchronizer {
/**
* 将state分为高16位和低16位两个无符号的值
* 高16位进行读锁的计数,用其中的低16位表示写锁的状态和重入次数
* SHARED_SHIFT:分隔的位数
* SHARED_UNIT:1左移SHARED_SHIFT位,读锁+1时,因为读锁用的是高16位所以读锁+1即加SHARED_UNIT(0000000000000001 0000000000000000)
* MAX_COUNT:最大的读数量,将1左移SHARED_SHIFT位减1,即0000000000000001 0000000000000000减1
* EXCLUSIVE_MASK:用于获取写锁的重入次数,写锁用低16位表示,EXCLUSIVE_MASK(0000000000000000 1111111111111111)& int c,假如c = 1
* 即&0000000000000001 得出结果1
* (下有详解)
*/
static final int SHARED_SHIFT = 16;
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
//获取读锁的数量
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
//获取写锁的重入次数
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
//用ThreadLocal记录读锁重入的次数
static final class HoldCounter {
int count = 0;
//使用线程的id避免垃圾回收产生的问题
final long tid = getThreadId(Thread.currentThread());
}
static final class ThreadLocalHoldCounter
extends ThreadLocal<HoldCounter> {
public HoldCounter initialValue() {
return new HoldCounter();
}
}
//此处不明白的话请阅读下面详解
//当前线程持有的可重入锁的数量
private transient ThreadLocalHoldCounter readHolds;
//成功获取 readLock 的最后一个线程的保持计数
private transient HoldCounter cachedHoldCounter;
/**
* firstReader 是第一个获得读锁的线程。
* firstReaderHoldCount 是 firstReader 的保留计数
* 只有一个线程获取到了锁,则用firstReaderHoldCount计数
*/
private transient Thread firstReader = null;
private transient int firstReaderHoldCount;
/**
*构造方法
*/
Sync() {
readHolds = new ThreadLocalHoldCounter();
setState(getState()); // ensures visibility of readHolds
}
/**
* 尝试读锁上锁
*/
protected final int tryAcquireShared(int unused) {
//获取当前需要上锁的线程
Thread current = Thread.currentThread();
//获取状态值
int c = getState();
/**
* 1.判断是否有写锁
* 2.如果有写锁,判断当前线程是否是拥有独占访问权限的线程
* 3.如果不是返回-1,AQS会调用后续方法将当前线程加入到等待队列中
*/
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
//没有写线程或有写线程但当前线程是拥有独占访问权限的线程(当前执行的线程是读线程)
int r = sharedCount(c); //获取当前读锁数量
/**
* 1.判断当前读线程是否应该被上锁执行
* 2.如果应该被上锁执行,判断当前读线程数是否小于最大线程数
* 3.如果当前线程应该被上锁执行,并且当前线程数小于最大线程数,则比较并替换读锁的状态值
*/
if (!readerShouldBlock() && //readerShouldBlock()判断当前读线程是否应该被上锁执行,该方法在公平锁模式下会判断等待队列里是否有线程,非公平锁模式下判断等待队列里是否有写线程
r < MAX_COUNT && //线程数是否小于最大线程数
compareAndSetState(c, c + SHARED_UNIT)) { //更新读锁状态值
if (r == 0) { //如果当前线程数为0
firstReader = current; //将当前线程赋值给firstReader 记录第一个获取读锁的线程
firstReaderHoldCount = 1; //第一个获取读锁的线程记录重入次数
} else if (firstReader == current) { //如果当前读线程数不为0且当前线程是第一读锁线程
firstReaderHoldCount++; //重入次数++
} else { //如果当前读线程数不为0且不是第一个读线程
HoldCounter rh = cachedHoldCounter; //获取当前线程缓存的计数对象
//判断rh是否为null,或当前线程与缓存中的线程是否是同一个
//若rh为空或线程不对应,则重新从线程容器中取
if (rh == null || rh.tid != getThreadId(current)) //最后一个读线程不存在,或者最后一个读线程存在但是当前线程的id和记录的最后一个读线程不一致
cachedHoldCounter = rh = readHolds.get(); //重新从线程容器中取
else if (rh.count == 0) //最后一个读线程存在并且当前线程的id和记录的最后一个读线程一致,但是里面的计数为0
readHolds.set(rh); //更新HoldCounter对象
rh.count++; //重入数量++
}
return 1; //返回获取锁成功
}
return fullTryAcquireShared(current); //以上是优化,fullTryAcquireShared()该方法执行的是读锁上锁的完整版
}
/**
* 读锁上锁完整版
*/
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
for (;;) {
int c = getState(); //获取当前锁的状态值
/**
* 判断是否有写线程,或者当前线程可不可以被上锁,
* 1.如果有写线程,但是当前线程没有独占访问权限,则加入等待队列,如果有独占访问权限,则表明当前线程要降锁级,即原来获取的是写入锁,现在重入后要执行的是读操作,要获取读锁
* 2.没有写线程,等待队列里面有线程等待,
* 第一个读线程不是当前线程,计数线程计数值为0,移除掉计数线程,并进入等待队列
*/
if (exclusiveCount(c) != 0) { //有写锁
if (getExclusiveOwnerThread() != current)//独占访问权限的线程不是当前线程
return -1; //由AQS加入等待队列
} else if (readerShouldBlock()) { //有等待的写锁
if (firstReader == current) { //第一个读线程是当前线程
} else { //第一个读线程不是当前线程
if (rh == null) { //计数线程为空
rh = cachedHoldCounter; //获取当前线程缓存的计数对象
if (rh == null || rh.tid != getThreadId(current)) { //最后一个计数线程为空或者最后一个计数线程的id不是当前线程的计数线程
rh = readHolds.get(); //获取当前线程的计数线程
if (rh.count == 0) //如果重入次数为0
readHolds.remove(); //移除掉计数线程
}
}
if (rh.count == 0) //重入次数为0,将当前线程加入等待队列中
return -1;
}
}
if (sharedCount(c) == MAX_COUNT) //当前读线程数量为最大读线程数量
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) { //设置读线程数量
if (sharedCount(c) == 0) { //当前读线程数为0
firstReader = current; //当前线程记录为第一个读线程
firstReaderHoldCount = 1; //重入数 = 1
} else if (firstReader == current) { //当前线程是第一个读线程
firstReaderHoldCount++; //重入数++
} else {
if (rh == null)
rh = cachedHoldCounter;//获取当前线程缓存的计数对象
if (rh == null || rh.tid != getThreadId(current)) //当前线程不是最后一个读线程
rh = readHolds.get(); //获取当前线程的计数线程
else if (rh.count == 0) //计数为0,设置进readHolds
readHolds.set(rh);
rh.count++; //添加重入次数
cachedHoldCounter = rh; // 记录最后的读线程
}
return 1;
}
}
}
/**
* 读锁解锁
*/
protected final boolean tryReleaseShared(int unused) {
//获取当前线程
Thread current = Thread.currentThread();
//开始处理锁重入的数据
//当前线程是否为第一个读线程
if (firstReader == current) {
if (firstReaderHoldCount == 1) //如果第一个读锁的重入次数为1
firstReader = null; //第一个读线程记录为null
else
firstReaderHoldCount--; //重入次数--
} else {
HoldCounter rh = cachedHoldCounter; //获取当前线程缓存的计数对象
if (rh == null || rh.tid != getThreadId(current)) //最后一个读线程不是当前线程
rh = readHolds.get(); //获取当前线程的重入计数
int count = rh.count; //重入次数
if (count <= 1) { //如果小于一次,清除重入次数
readHolds.remove();
if (count <= 0) //重入次数小于0,抛异常
throw unmatchedUnlockException();
}
--rh.count; //重入次数--
}
for (;;) {
int c = getState(); //获取当前锁状态
int nextc = c - SHARED_UNIT; //读锁状态-1
if (compareAndSetState(c, nextc)) //设置新的读锁锁状态
return nextc == 0; //如果设置成功,判断锁是否全部释放完毕,完毕由AQS唤醒等待线程
}
}
/**
* 写锁上锁
*/
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread(); //获取当前线程
int c = getState(); //获取锁状态
int w = exclusiveCount(c); //获取写锁个数
if (c != 0) { //有线程获得到锁,
//w == 0表明写锁为0 同时c!=0表明有读锁,此时写锁上锁失败,
//写锁数量不为0,表明此时有写锁,同时当前线程不是持有独占访问权限的线程,则上锁失败
if (w == 0 || current != getExclusiveOwnerThread()) //读锁个数为0,或者读锁个数不为0但当前线程不是持有独占访问权限的线程
return false; //上锁失败
//此下的逻辑是有写锁,同时当前线程是只有独占访问权限的线程,设置锁的状态(锁重入)
if (w + exclusiveCount(acquires) > MAX_COUNT) //超出了锁的最大数量
throw new Error("Maximum lock count exceeded");
setState(c + acquires); //设置锁状态
return true; //获取锁成功
}
//如果当前线程是第一个获取锁的线程
if (writerShouldBlock() || //判断是否可以获取锁
!compareAndSetState(c, c + acquires)) //设置锁状态
return false;
//可以获取锁,设置锁状态成功
setExclusiveOwnerThread(current); //将当前线程设置为持有独占访问权限的线程
return true;
}
/**
* 写锁释放锁
*/
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively()) //当前线程是否为独占访问权限的线程
throw new IllegalMonitorStateException(); //不是,抛出异常
int nextc = getState() - releases; //计算释放后的锁状态
boolean free = exclusiveCount(nextc) == 0; //写锁的个数是不是为0
if (free) //为0,全部释放完毕
setExclusiveOwnerThread(null); //独占访问权限的线程设置为null
setState(nextc); //设置状态
return free; //是否唤醒等待队列的线程
}
}
NonfairSync
/**
* 非公平锁
*/
static final class NonfairSync extends Sync {
//非公锁的状态下,是否应该获取写锁的方法始终返回false,非公平模式下读锁一上来就会抢锁
final boolean writerShouldBlock() {
return false;
}
//非公平状态下的是否应该获取写锁,如果等待队列中有写锁,返回true
final boolean readerShouldBlock() {
return apparentlyFirstQueuedIsExclusive();
}
}
FairSync
/**
* 公平锁,判断等待队列中是否有线程等待
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -2274990926593161451L;
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
final boolean readerShouldBlock() {
return hasQueuedPredecessors();
}
}
ReadLock
public static class ReadLock implements Lock, java.io.Serializable {
//同步器
private final Sync sync;
//构造方法,传入ReentrantReadWriteLock 的同步器
protected ReadLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
//上锁的方法
public void lock() {
sync.acquireShared(1);
}
//解锁的方法
public void unlock() {
sync.releaseShared(1);
}
}
WriteLock
public static class WriteLock implements Lock, java.io.Serializable {
//同步器
private final Sync sync;
//构造方法
protected WriteLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
//上锁
public void lock() {
sync.acquire(1);
}
//解锁
public void unlock() {
sync.release(1);
}
}
ReentrantLock主要用于读多写少的场景下,特点是一个资源可以同时被多个只读线程访问,或者一个只写线程访问,但读写操作不能同时进行。
ReentrantReadWriteLock内部维护着两个锁,一个读锁,一个写锁。读锁可以同时被多个线程获取,而写锁同一时刻只能被一个线程获取,并且一旦写锁被使用,其他所有读线程也将被阻塞。读锁是共享锁,写锁则是独占锁。