简介
ReentrantLock是排他锁,在高并发读多写少的场景下,同时还要保证线程安全,如果使用ReentrantLock的效率不是那么好,所以才有了ReentrantReadWriteLock。
在讲源码之前,我们对读写锁要有个认知。
读读操作是共享的。
读写操作是互斥的。
写写操作是互斥的。
写读操作是互斥的。(写读两个不同线程)
单线程获取写锁后,再次获取读锁,可以拿到。(写读操作可以重入)
单线程获取读锁后,再次获取写锁,不可以拿到。(A线程拿到读锁,然后在获得写锁修改数据,由于读锁共享,这时B线程拿到读锁读取数据显然数据是有可能是脏数据)
使用方法
public class XxxTest {
// 读写锁!
static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
// 写锁
static ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
// 读锁
static ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
public static void main(String[] args) throws InterruptedException {
readLock.lock();
try {
System.out.println("拿到读锁!");
} finally {
readLock.unlock();
}
writeLock.lock();
try {
System.out.println("拿到写锁!");
} finally {
writeLock.unlock();
}
}
}
源码解析
首先,ReentrantReadWriteLock也是基于AQS实现的,跟ReentrantLock类似,如果不了解,可以看下我的另一篇博客ReetrantLock源码分析,这样学习起来事半功倍。
跟ReetrantLock一样,是否拿到锁资源在于修改AQS的state的值,只不过在ReentrantReadWriteLock中,state的高16位是读锁,低16位是写锁。
从ReentrantReadWriteLock的构造方法看到默认是非公平锁
写锁加锁源码
来到AQS的acquire方法,除了tryAcquire方法,其它都跟ReentrantLock一样,我们直接看ReentrantReadWriteLock的tryAcquire方法
protected final boolean tryAcquire(int acquires) {
/*
* Walkthrough:
* 1. If read count nonzero or write count nonzero
* and owner is a different thread, fail.
* 2. If count would saturate, fail. (This can only
* happen if count is already nonzero.)
* 3. Otherwise, this thread is eligible for lock if
* it is either a reentrant acquire or
* queue policy allows it. If so, update state
* and set owner.
*/
//获取当前线程
Thread current = Thread.currentThread();
// 获取state
int c = getState();
//获取state低16位写锁
int w = exclusiveCount(c);
// 如果c不等于0,说明要么有读锁,要么有写锁
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;
}
//c=0 锁无人持有
//公平锁看下要不要排队writerShouldBlock->hasQueuedPredecessors()队列有排队返回true,否则反之
//非公平锁开抢,writerShouldBlock直接返回false
if (writerShouldBlock() ||
//走到这说明不用排队,直接进行CAS开抢
!compareAndSetState(c, c + acquires))
//CAS失败,返回false
return false;
//设置锁持有者为自己
setExclusiveOwnerThread(current);
return true;
}
剩下的acquireQueued跟addWaiter都是AQS的方法,跟ReentrantLock一样。
写锁释放锁源码
其中unparkSuccessor方法跟ReentrantLock一样,我们直接看tryRelease方法
protected final boolean tryRelease(int releases) {
//不是线程持有者释放锁,直接抛异常
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//state - 1
int nextc = getState() - releases;
//判断低16位写锁是否为0
boolean free = exclusiveCount(nextc) == 0;
if (free)
//线程持有者为null
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}
读锁加锁源码
protected final int tryAcquireShared(int unused) {
/*
* Walkthrough:
* 1. If write lock held by another thread, fail.
* 2. Otherwise, this thread is eligible for
* lock wrt state, so ask if it should block
* because of queue policy. If not, try
* to grant by CASing state and updating count.
* Note that step does not check for reentrant
* acquires, which is postponed to full version
* to avoid having to check hold count in
* the more typical non-reentrant case.
* 3. If step 2 fails either because thread
* apparently not eligible or CAS fails or count
* saturated, chain to version with full retry loop.
*/
//获取当前线程
Thread current = Thread.currentThread();
//获取state
int c = getState();
//如果低16位不等于0说明有写锁 而且写锁持有者不是自己,去排队
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
//获取高16位读锁次数
int r = sharedCount(c);
//公平锁,readerShouldBlock如果有人排队返回ture
//非公平锁,readerShouldBlock->apparentlyFirstQueuedIsExclusive->看完队列头的next是否是写锁在排队
//因为读锁CAS是肯定成功的,如果读锁直接不顾排队的写锁直接CAS
//那么当一直有读锁进来,写锁永远都在排队
if (!readerShouldBlock() &&
r < MAX_COUNT &&
//CAS对高16位加1
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {
使用firstReader存储第一个拿到读锁的线程
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
//我是第一个拿到读锁线程的,现在进行锁重入
firstReaderHoldCount++;
} else {
//不是第一个拿到读锁的
//获得最后一个读锁线程
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
//进来说明我是最后一个或者第二个来的,将自己赋值给cachedHoldCounter
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
//之前来过,没拿过读锁,然后突然走了,现在再来,赋值为0
readHolds.set(rh);
//重入次数加1
rh.count++;
}
return 1;
}
return fullTryAcquireShared(current);
}
//进入这个方法说明在state低16位为0的情况下拿不到读锁
final int fullTryAcquireShared(Thread current) {
/*
* This code is in part redundant with that in
* tryAcquireShared but is simpler overall by not
* complicating tryAcquireShared with interactions between
* retries and lazily reading hold counts.
*/
HoldCounter rh = null;
for (;;) {
获取state
int c = getState();
//有写锁
if (exclusiveCount(c) != 0) {
//不是自己
if (getExclusiveOwnerThread() != current)
return -1;
// else we hold the exclusive lock; blocking here
// would cause deadlock.
//公平锁,readerShouldBlock如果有人排队返回ture
//非公平锁,readerShouldBlock->apparentlyFirstQueuedIsExclusive->看完队列头的next是否是写锁在排队
//因为读锁CAS是肯定成功的,如果读锁直接不顾排队的写锁直接CAS
//那么当一直有读锁进来,写锁永远都在排队
//readerShouldBlock读锁应该阻塞,进入逻辑,说明是阻塞相关逻辑
} else if (readerShouldBlock()) {
// Make sure we're not acquiring read lock reentrantly
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
} else {
if (rh == null) {
//获取最后一个拿到读锁的
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) {
//获取自己重入次数
rh = readHolds.get();
//如果我的次数是0,说明不是重入操作
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给state高16位加1
if (compareAndSetState(c, c + SHARED_UNIT)) {
//下面给tryAcquireShared一样
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;
}
}
}
当tryAcquireShared返回-1时,执行doAcquireShared方法
如果当前读锁节点排队第一,尝试获取锁,否则挂起
private void doAcquireShared(int arg) {
//添加共享锁节点到队列中
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//获取上一个节点
final Node p = node.predecessor();
if (p == head) {
//如果是头,尝试获取锁
int r = tryAcquireShared(arg);
if (r >= 0) {
//r大于=0,说明获取锁成功唤醒队列中要获取读锁的线程
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
//找到前面为-1的节点,才能执行挂起线程
if (shouldParkAfterFailedAcquire(p, node) &&
//调用Unsafe类的park方法
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
读锁释放锁代码
protected final boolean tryReleaseShared(int unused) {
获取当前线程
Thread current = Thread.currentThread();
如果第一个获得读锁的是当前线程
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
//如果重入次数为1
if (firstReaderHoldCount == 1)
将第一个获得读锁线程置为空
firstReader = null;
else
重入次数减1
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();
}
将锁重入次数减1
--rh.count;
}
for (;;) {
获得state
int c = getState();
state高位减1
int nextc = c - SHARED_UNIT;
// CAS设置state
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;
}
}
当tryReleaseShared返回true,此时state为0,既没有读锁也没有写锁获取到锁资源,调用doReleaseShared方法
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
//CAS将头节点状态从-1变为0
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
//唤醒队列最前面状态为-1的节点
unparkSuccessor(h);
}
else if (ws == 0 &&
//CAS将头节点从0变成-3
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
本人能力有限,如果有错,欢迎各位指出。