本文对读写锁源码进行了比较详细的分析。
另外可以参考可重入锁 ReentrantLock 的分析:Java并发Concurrent包的锁(三)——ReentrantLock源码分析
ReadWriteLock接口
ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有 writer,读取锁可以由多个 reader 线程同时保持。写入锁是独占的。
public interface ReadWriteLock {
// 获得读锁
Lock readLock();
// 获得写锁
Lock writeLock();
}
与互斥锁相比,读-写锁允许对共享数据进行更高级别的并发访问。写线程虽然一次只有一个访问数据,但读线程可以同时读取,而在实际中,读取往往都是大量的,写入是偶尔的,读-写锁利用了这一点。从理论上讲,与互斥锁相比,使用读-写锁所允许的并发性增强将带来更大的性能提高。在实践中,只有在多处理器上并且只在访问模式适用于共享数据时,才能完全实现并发性增强。
如果数据不常修改,而主要用于搜索,适合使用读写锁;而如果数据更新十分频繁,数据大部分时间都被每个线程所独占,这种情况并发也提高不了太多效率。而且如果读取的操作用时太多,那么读写锁本身的开销就是最大的执行成本。所以具体问题还是要具体分析。
ReentrantReadWriteLock的Sync
读写锁引用了 Sync 类,和 ReentrantLock 的 Sync 一样是通过 AbstractQueuedSynchronizer 进行扩展的,下面对其代码进行分析:
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 6317671515068378041L;
/*
* 锁状态state是int型,读写分为两部分,各用一个无符号short来表示
* 低位的表示写锁即独享锁,高位的表示读锁共享数
*/
// 这个16是两个字节,一个short的长度
static final int SHARED_SHIFT = 16;
// 1左移16位,为00000000 00000001 00000000 00000000
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
// 1左移16位,减1,为65535,读写的获取锁的最大次数
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
// 右移16位,获取c高位的16位,表示读锁的共享总数(高位2字节全0)
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
// 和两个字节的全1字节与操作,高位2字节全0,获取写锁的总数
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
// 每个线程的计数器,保存读锁共享数
static final class HoldCounter {
int count = 0;
final long tid = getThreadId(Thread.currentThread());
}
// ThreadLocal的子类
static final class ThreadLocalHoldCounter
extends ThreadLocal<HoldCounter> {
// 初始化方法,返回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
}
// 读锁的阻塞
abstract boolean readerShouldBlock();
// 写锁的阻塞
abstract boolean writerShouldBlock();
// 释放写锁
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
// 当前线程不是owner线程,不能进行释放
throw new IllegalMonitorStateException();
// 计算释放后的状态值
int nextc = getState() - releases;
// 如果当前写锁释放后,看是否还有写锁,没有写锁返回true
boolean free = exclusiveCount(nextc) == 0;
if (free)
// 没有写锁了将锁对象的当前owner置为null
setExclusiveOwnerThread(null);
// 更新state的新值
setState(nextc);
return free;
}
// 尝试获取写锁
protected final boolean tryAcquire(int acquires) {
// 当前线程
Thread current = Thread.currentThread();
// 状态c,包含读锁和写锁的总数
int c = getState();
// w表示写锁的总数
int w = exclusiveCount(c);
if (c != 0) {
// 如果写锁为0,说明当前有读锁(也可能是当前线程有读锁),不能获取写锁
// 如果写锁不为0,但是当前线程不是owner线程不可重入,不能获取写锁
if (w == 0 || current != getExclusiveOwnerThread())
return false;
// 写锁数量超过了65535报错
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// 这里已经说明写锁是当前线程可重入的了,直接设置state值,返回true
setState(c + acquires);
return true;
}
// c==0时,如果写需要阻塞或者设置状态失败,返回false
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
// 设置锁的owner独占标识
setExclusiveOwnerThread(current);
return true;
}
// 尝试释放读锁
protected final boolean tryReleaseShared(int unused) {
// 当前线程current
Thread current = Thread.currentThread();
// 如果当前线程是第一个获取读锁的
if (firstReader == current) {
// 当前线程读锁总数为1,直接将firstReader置为null
if (firstReaderHoldCount == 1)
firstReader = null;
// 如果不为1,那么减掉1即可
else
firstReaderHoldCount--;
} else {
// 最后一个获取读锁的线程
HoldCounter rh = cachedHoldCounter;
// 如果当前线程也不是最后一个获取读锁的线程,从ThreadLocal中取
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
// 当前线程读锁的个数
int count = rh.count;
// 如果count==1,释放了就为0,直接在ThreadLocal中去掉当前线程
if (count <= 1) {
readHolds.remove();
// count<=0,此时抛出不匹配释放锁的异常
if (count <= 0)
throw unmatchedUnlockException();
}
// 正常情况下,将当前线程的读锁count值减1即可。
--rh.count;
}
for (;;) {
int c = getState();
// 高位减1,新的读写锁值
int nextc = c - SHARED_UNIT;
// 尝试设置state值,预测之前为c,更新为nextc
if (compareAndSetState(c, nextc))
// 释放读锁不会影响其他读锁,但是如果这时读写锁都空闲,等待中的写线程将开始执行
// 这里只有读写锁都释放了,才返回true
return nextc == 0;
}
}
// 释放读锁的不匹配异常
private IllegalMonitorStateException unmatchedUnlockException() {
return new IllegalMonitorStateException(
"attempt to unlock read lock, not locked by current thread");
}
// 尝试获取读锁
protected final int tryAcquireShared(int unused) {
// 当前线程
Thread current = Thread.currentThread();
// 锁的state值
int c = getState();
// 如果有独占标识同时当前线程并不是独占线程时,直接返回-1
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
// 读锁的总数
int r = sharedCount(c);
// 如果读不需要阻塞,同时读锁小于65535,并且能够CAS设置c的值读个数加1
// 这里的阻塞策略readerShouldBlock进行了判断,有两种情况:
// 1.公平模式:如果AQS队列前面有等待的结点,当前线程应该阻塞
// 2.非公平模式:如果AQS前面有线程在等待写锁,当前线程应该阻塞(这样做的原因是为了防止写饥饿)。
// 出现这个阻塞情况,就不走下边的if了,直接到最后走fullTryAcquireShared方法处理
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
// 读锁个数为0,将当前线程作为第一个读线程单独保存,不存在ThreadLocal中
if (r == 0) {
firstReader = current;
// 当前线程即第一个读线程的读锁总数置为1
firstReaderHoldCount = 1;
// 如果r!=0,说明有读线程了,判断当前线程是不是第一个读线程
} else if (firstReader == current) {
// 如果当前线程就是第一个读线程,数量count加1即可
firstReaderHoldCount++;
} else {
// 当前线程不是第一个读线程,判断是不是最后一个读线程
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
// 当前线程不是最后一个读线程,从ThreadLocal中取线程
// 同时将最后一个读线程更新为当前线程
cachedHoldCounter = rh = readHolds.get();
// 这里如果当前线程就是最后一个读线程的话,如果count为0
else if (rh.count == 0)
// 需要重新set当前线程(之前释放时,readHolds.remove()进行过这个操作)
readHolds.set(rh);
// 将当前线程的count加1
rh.count++;
}
// 返回值1说明获取读锁成功了
return 1;
}
// 读线程阻塞时,执行这个方法
return fullTryAcquireShared(current);
}
// 该方法处理读线程阻塞的情况
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
for (;;) {
int c = getState();
// 独占标记存在且当前owner不是当前线程,不能重入
if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
// 没有写锁存在时,如果读阻塞
// 这里的阻塞策略,如果:1.公平模式:如果当前AQS队列前面有等待的结点,返回false;2.非公平模式:如果
// AQS前面有线程在等待写锁,返回false(这样做的原因是为了防止写饥饿)。
} else if (readerShouldBlock()) {
// 如果当前线程是第一个读线程
if (firstReader == current) {
} else {
if (rh == null) {
// 优先赋值成上一次获取读锁成功的cache,即最后一个读锁
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) {
// 如果和当前线程不匹配,从ThreadLocal中取
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
}
}
// 不是读锁重入的情况,返回-1
if (rh.count == 0)
return -1;
}
}
// 如果读锁总数为65535,报错
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// 如果CAS将c的读锁总数增加1成功
if (compareAndSetState(c, c + SHARED_UNIT)) {
// 如果读锁数量为0,当前线程作为第一个线程存起来
if (sharedCount(c) == 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)
readHolds.set(rh);
rh.count++;
cachedHoldCounter = rh; // cache for release
}
return 1;
}
}
}
// 和tryAcquire基本一致,去掉了考虑阻塞部分,相当于非公平模式的获取写锁
final boolean tryWriteLock() {
Thread current = Thread.currentThread();
int c = getState();
if (c != 0) {
int w = exclusiveCount(c);
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
}
if (!compareAndSetState(c, c + 1))
return false;
setExclusiveOwnerThread(current);
return true;
}
// 和tryAcquireShared函数基本一致,去掉了考虑阻塞部分,相当于非公平模式的获取读锁
final boolean tryReadLock() {
Thread current = Thread.currentThread();
for (;;) {
int c = getState();
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return false;
int r = sharedCount(c);
if (r == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (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 true;
}
}
}
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
final Thread getOwner() {
return ((exclusiveCount(getState()) == 0) ?
null :
getExclusiveOwnerThread());
}
final int getReadLockCount() {
return sharedCount(getState());
}
final boolean isWriteLocked() {
return exclusiveCount(getState()) != 0;
}
final int getWriteHoldCount() {
return isHeldExclusively() ? exclusiveCount(getState()) : 0;
}
final int getReadHoldCount() {
if (getReadLockCount() == 0)
return 0;
Thread current = Thread.currentThread();
if (firstReader == current)
return firstReaderHoldCount;
HoldCounter rh = cachedHoldCounter;
if (rh != null && rh.tid == getThreadId(current))
return rh.count;
int count = readHolds.get().count;
if (count == 0) readHolds.remove();
return count;
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
readHolds = new ThreadLocalHoldCounter();
setState(0); // reset to unlocked state
}
final int getCount() { return getState(); }
}
NonfairSync和FairSync
非公平模式
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -8159625535654395037L;
final boolean writerShouldBlock() {
// 不公平模式下,写锁应该始终可以插队,写线程不阻塞
return false;
}
final boolean readerShouldBlock() {
// 不公平模式下,如果队列前端有写线程等待,那么读应该阻塞
return apparentlyFirstQueuedIsExclusive();
}
}
// 当第一个节点的thread是等待写锁时,返回true
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
return (h = head) != null &&
(s = h.next) != null &&
!s.isShared() &&
s.thread != null;
}
公平模式
static final class FairSync extends Sync {
private static final long serialVersionUID = -2274990926593161451L;
final boolean writerShouldBlock() {
// 公平模式,有等待的其它线程,写应该阻塞
return hasQueuedPredecessors();
}
final boolean readerShouldBlock() {
// 公平模式,有等待的其它线程,读应该阻塞
return hasQueuedPredecessors();
}
}
// 公平模式下,判断队列前边的节点是否有等待的其它线程(非当前线程不能重入),返回true
public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}