本文将介绍Java中ReentrantReadWriteLock的实现原理,从JDK源码层面讲解读写锁的加锁、释放锁的流程,最后对流程进行总结。
读写锁概述
读写锁 ReentrantReadWriteLock 的依赖关系如下图所示。
读写锁的基本使用如下
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
readLock.lock();
readLock.unlock();
ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
writeLock.lock();
writeLock.unlock();
读锁和写锁使用同一个 Sync 同步器,即使用同一个等待队列和 state。读锁状态使用 state 高 16 位存储,写锁状态使用 state 低 16 位存储。
读锁涉及两个重入计数:state(高 16 位)用于记录有多少个线程持有该读锁,HoldCounter 用于记录当前线程重入该读锁多少次,每个线程均有一个对应的 HoldCounter 对象。
写锁涉及一个重入计数:state(低 16 位)用于记录有多少个线程持有该写锁。
读锁不支持条件变量,写锁支持条件变量。
写锁加锁流程
WriteLock 的 lock() 方法会调用同步器的 acquire()方法。
// WriteLock implements Lock,
public void lock() {
sync.acquire(1);
}
同步器的 acquire() 方法会先调用 tryAcquire() 方法尝试获取写锁,获取写锁失败则调用 AbstractQueuedSynchronizer 的全参 acquire() 方法将线程加入等待队列。
// AbstractQueuedSynchronizer
public final void acquire(int arg) {
if (!tryAcquire(arg))
// 获取写锁失败则将线程加入等待队列
acquire(null, arg, false, false, false, 0L);
}
在介绍 ReentrantLock 的实现原理时,已对 AbstractQueuedSynchronizer 的全参 acquire() 方法进行了较为详细的介绍,可参照 [源码解读 | Java中ReentrantLock的实现原理,此处不再赘述,重点来看一下 tryAcquire() 方法如何获取写锁。
// Sync extends AbstractQueuedSynchronizer
@ReservedStackAccess
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
// 写锁状态
int w = exclusiveCount(c);
// 已加写锁或者读锁
if (c != 0) {
if (
// 已加读锁:读写互斥,不能再加写锁
w == 0 ||
// 已加写锁,但不是自己加的(非写锁重入):写写互斥,不能再加写锁
current != getExclusiveOwnerThread()
)
// 加写锁失败
return false;
// 已加写锁且是自己加的
// 整数溢出
if (w +