概述
ReentrantReadWriteLock是一个可重入的读写锁,写锁独占,读锁共享,支持公平和非公平两种方式加锁,源码可以看到,内部的Sync类重写了AQS中的tryAcquire,tryAcquireShared方法已经对应的释放方法。
独占式锁和共享式锁都已经分析很多了,今天我们简单看下ReentrantReadWriteLock的源码。测试例子依然可以在github中看到
ReentrantReadWriteLock 类结构
public class ReentrantReadWriteLock
implements ReadWriteLock, java.io.Serializable {
//读锁
private final ReentrantReadWriteLock.ReadLock readerLock;
//写锁
private final ReentrantReadWriteLock.WriteLock writerLock;
//AQS的子类
final Sync sync;
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
...
}
我们知道锁的实现都依赖于Sync这个AQS的子类,一起看看Sync的类结构吧
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 6317671515068378041L;
//state的值 高16位表示读锁(或者共享锁)的次数, 低16位表示写锁(独占锁)的次数(注意重入也会累加)
static final int SHARED_SHIFT = 16;
//所以读锁加锁成功一次会加一个SHARED_UNIT
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
//最大加锁量
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
//写锁计算的因子,高16位全是0,低16位全是1,state值相与就是写锁量
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
//读锁量或者叫共享量 因为是高16位表示,计算具体值是左移16位
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
//计算写锁量 将state和EXCLUSIVE_MASK相与计算写锁量
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
//线程占用的读锁量(重入性)
static final class HoldCounter {
int count = 0;
// Use id, not reference, to avoid garbage retention
final long tid = getThreadId(Thread.currentThread());
}
static final class ThreadLocalHoldCounter
extends ThreadLocal<HoldCounter> {
public HoldCounter initialValue() {
return new HoldCounter();
}
}
private transient ThreadLocalHoldCounter readHolds;
//记录最新获取读锁线程的缓存计数
private transient HoldCounter cachedHoldCounter;
//第一个获取读锁的线程
private transient Thread firstReader = null;
//第一个获取读锁线程的计数
private transient int firstReaderHoldCount;
Sync() {
readHolds = new ThreadLocalHoldCounter();
setState(getState()); // ensures visibility of readHolds
}
。。。
}
我们以非公平的读锁加锁过程举例:\
public void lock() {
sync.acquireShared(1);
}
protected final int tryAcquireShared(int unused) {
/*
* 1.如果被其他线程加了写锁,直接返回失败
* 2. 否则线程不应该阻塞,没达到最大值,进行CAS增加State数量
* 成功则加锁成功,如果是第一线程加锁赋值firstReader ,
* 如果不是第一个线程,判断最新的读锁线程是不是自己,不是则将自己赋给最新线程,
* 增加HoldCounter计数(本线程的读锁量)
* 3. 如果第二步失败,走完整的CAS替换流程
*/
Thread current = Thread.currentThread();
int c = getState();
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
int r = sharedCount(c);
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
return fullTryAcquireShared(current);
}
readerShouldBlock是一个抽象方法,公平子类和非公平子类复写该方法,我们看下非公平子类的readerShouldBlock方法逻辑
final boolean readerShouldBlock() {
return apparentlyFirstQueuedIsExclusive();
}
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
//如果队列中第一个是写等待,则返回true
//防止写等待一直被阻塞着,毕竟读是共享的,人多抢的多
return (h = head) != null &&
(s = h.next) != null &&
!s.isShared() &&
s.thread != null;
}
上面我们看到如果第二步没有抢到读锁则进入充分请求锁阶段fullTryAcquireShared
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
for (;;) {
int c = getState();
//如果其他线程占了写锁,返回加锁失败
if ((c) != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
} else if (readerShouldBlock()) {
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
} else {
//不是重入的,则返回-1
if (rh == null) {
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) {
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
}
}
if (rh.count == 0)
return -1;
}
}
//到了这里就是应该加读锁的
if (sharedCount(c) == MAX_COUNT) //读锁量最大后抛出异常
throw new Error("Maximum lock count exceeded");
//进行CAS替换,替换成功则加锁成功,进行必要的属性赋值就好了(和上面的快速加锁逻辑一致)
//替换失败,其他线程读锁加成功了,for循环再来一次
if (compareAndSetState(c, c + SHARED_UNIT)) {
if (sharedCount(c) == 0) {
firstReader = current;
firstReaderHoldCount = 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)
readHolds.set(rh);
rh.count++;
cachedHoldCounter = rh; // cache for release
}
return 1;
}
}
}
上面就是加非公平读锁的逻辑了,要先看AQS源码篇哦。
公平读锁的逻辑只有readerShouldBlock方法不一样,公平读锁的readerShouldBlock方法,只要该线程不是队列的第二节点,就返回true,这里就不详细看了。
读锁的释放
public void unlock() {
sync.releaseShared(1);
}
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
//进行本线程的读锁量操作,如果是firstReader操作firstReader,否则得到HoldCounter操作
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
if (firstReaderHoldCount == 1)
firstReader = 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)
throw unmatchedUnlockException();
}
--rh.count;
}
//CAS操作state, state减为0,读锁完全释放,唤起后继节点
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
// Releasing the read lock has no effect on readers,
// but it may allow waiting writers to proceed if
// both read and write locks are now free.
return nextc == 0;
}
}
下面我们看看写锁的加锁过程
public void lock() {
sync.acquire(1);
}
protected final boolean tryAcquire(int acquires) {
/*
* Walkthrough:
* 1. state不为0,说明被加了读锁或写锁,判断写锁为0或者写锁不是自己,则返回失败.
* 2. 如果写锁满了返回失败,如果是自己重入的写锁,直接修改state的值,返回加锁成功
* 3. 如果写锁应该阻塞或者CAS设置state失败(说明加锁失败),返回false.
* CAS 设置成功,设置独占线程为自己,返回加锁成功
*/
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
setState(c + acquires);
return true;
}
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
非公平加写锁,writerShouldBlock总是返回false
final boolean writerShouldBlock() {
return false; // writers can always barge
}
公平加写锁也是只有writerShouldBlock方法不同,公平加写锁writerShouldBlock方法需要判断是否是第二节点。这里就不详细介绍了。
写锁的释放
public void unlock() {
sync.release(1);
}
protected final boolean tryRelease(int releases) {
//判断是否是独占线程
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//state减去释放量,如果减为0,说明全部写锁释放,返回true,进行后继节点的唤醒。
int nextc = getState() - releases;
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}