简介
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,</