文章目录
读写锁ReentrantReadWriteLock的原理
实际应用中,写少读多的情况很多,而使用ReentranLock性能太低(只有读锁时不需要限制其他读锁).ReentrantReadWriteLock应运而生,采用读写分离的策略,允许多个线程可以同时获取读锁.
(1). 结构
内部维护了一个ReadLock和一个WriteLock,它们都是依赖Sync实现具体功能,而Sunc继承自AQS.AQS中的state只维护了一个状态,而读写锁有两个锁,所以采用state的高16为表示读锁的状态,低16位表示写锁的状态
(2). 写锁的获取与释放
写锁使用WriteLock实现.
1). void lock()
写锁是个可重入的独占锁,当没有线程获取读锁且写锁空闲或者写锁属于当前线程时,可以获取
public void lock() {
sync.acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// 此方法在WriteLock(内部类)中实现
protected final boolean tryAcquire(int acquires) {
// 获取当前线程,写锁的重入次数W
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
// 写锁或者读锁已经被获取
if (c != 0) {
// w==0证明写锁空闲,那么就是读锁被获取,如果读锁空闲继而判断当前线程是否是写锁中的线程
if (w == 0 || current != getExclusiveOwnerThread())
// 进入这里说明读锁空闲,那么不能获取写锁.或者写锁重入的不是当前线程
return false;
// 走到这证明可以获取读锁没被获取,而且当前线程获取了写锁,这一步判断是否能继续进行重入
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// 设置重入值
setState(c + acquires);
return true;
}
// 第一个写线程获取写锁
// writerShouldBlock()当有别的线程也在获取此锁时,是否应该阻塞(分公平和非公平两种实现)
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
// 非公平锁,不需要判断,不阻塞
final boolean writerShouldBlock() {
return false; // writers can always barge
}
//公平锁,调用hasQueuedPredecessors()判断是否有前继节点,如果有说明不是队首,继续排队,放弃获取锁
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
2). void lockInterruptibly()
同样是获取锁,此方法会对中断进行响应.调用的是AQS中响应中断的获取锁方法.
3). boolean tryLock()
尝试获取写锁,没有获取到不会阻塞,逻辑同上.
4). boolean tryLock(long timeout, TimeUnit unit)
获取锁失败后挂起指定时间,如果时间到了之后还是没有获取到锁,返回false
5). void unlock()
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
// 上面都是AQS的内容,下面的方法是具体的锁实现的
protected final boolean tryRelease(int releases) {
//判断是否是写锁的拥有者调用的unlock
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
// 是当前线程获取的写锁
// 获取可重入值,写锁是低16位,可以直接加减.如果写锁重入次数为0,则锁应修改为空闲状态
int nextc = getState() - releases;
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}
(3). 读锁的获取与释放
读锁是使用ReadLock实现的.
1). void lock()
获取读锁,如果写锁是空闲的,其他线程都可以获取读锁.读锁是共享的
public void lock() {
sync.acquireShared(1);
}
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
// 具体的锁的实现
protected final int tryAcquireShared(int unused) {
// 获取当前线程,读锁标记
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 + SHARxianchengED_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;
}
// 没有获取成功的线程进入fullTryAcquireShared,逻辑与tryAcquireShared()类似,但是是自旋的
return fullTryAcquireShared(current);
}
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
for (;;) {
int c = getState();
// 写锁被占用
if (exclusiveCount(c) != 0) {
// 当前线程没有占有读锁
if (getExclusiveOwnerThread() != current)
return -1;
}
// 竞争时应不应该挂起自己
else if (readerShouldBlock()) {
// 此线程是第一个得到读锁的线程
if (firstReader == current) {
// doNothing
} else {
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");
// 获取锁
if (compareAndSetState(c, c + SHARED_UNIT)) {
// 当前锁空闲
if (sharedCount(c) == 0) {
firstReader = current;
firstReaderHoldCount = 1;
}
// 如果firstReader是当前线程,或者当前线程的cachedHoldCounter变量的count不为0(表示当前线程已经持有了该共享锁),均说明当前线程已经持有共享锁,此次获取共享锁是重入,这也是允许的,可以通过判断。
// 此处将重入次数分为fistReader的重入次数和其他所有线程的重入次数之和
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;
}
}
}
2). void lockInterruptibly()
可以对中断进行响应的获取锁.
3). boolean tryLock()
尝试获取锁,没有成功不会阻塞.
4). boolean tryLock(long timeout, TimeUnit unit)
如果超时时间内(挂起了)还没有获取锁,返回false.
5). void unlock()
public void unlock() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
// 具体锁实现的逻辑
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
// 对第一个获取锁的线程的重入次数进行更新
if (firstReader == current) {
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;
}
// 自旋的释放锁
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
// CAS成功则说明当前线程没有被其他线程打扰
if (compareAndSetState(c, nextc))
// 返回值为读锁是否空闲
return nextc == 0;
}
}
最后用一幅图加深理解