共计 3488 个字符,预计需要花费 9 分钟才能阅读完成。
ReadWriteLock 第二篇
提醒:看了 ReadWriteLock 第一篇 能力看这一篇,对于 ReadWriteLock 知识点明确上一篇讲的内容应酬个别面试没什么问题了。
1. hasQueuedPredecessors
上一篇在获取读共享锁流程中有一个判断 ,
if (!readerShouldBlock() &&
如果 readerShouldBlock 返回 false 那就失常获取锁,如果返回 true 那么就完结获取锁
这里说一下 偏心锁 这个办法的内容:
// ReentrantReadWriteLock.FairSync#readerShouldBlock
final boolean readerShouldBlock() {return hasQueuedPredecessors();
}
// 判断
// 如果有线程在以后线程获取锁之前排队 也就是队列曾经有元素了 但不是本人 返回 true (有人排队)
// 其余状况 返回 false(没人排队)public final boolean hasQueuedPredecessors() {
Node t = tail; // 队列尾指针
Node h = head;// 队列头指针
Node s;// 长期变量
return h != t && // 比拟 head tail 如果队列为空 h != t 为 false
((s = h.next) == null || // head 的 next 为空 证实队列为空
s.thread != Thread.currentThread() // 如果 head 有 next 也就是第一个期待线程 判断是不是本人 如果是本人 那嘿嘿);
}
2. 获取锁后一系列的设置 都有什么可说的
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++;
}
firstReader: 记录着第一个获取读锁的线程
firstReaderHoldCount:记录着第一个获取读锁的线程获取锁的次数
private transient Thread firstReader = null;// 线程
private transient int firstReaderHoldCount;// 数量
cachedHoldCounter:记录线程 ID+ 次数
存储在 ThreadLocal 中(面试弱援用 ThreadLocal 如果能扯到这里 你就赢一半人了)
每个获取读锁的线程都存着
static final class HoldCounter {
int count = 0;
// Use id, not reference, to avoid garbage retention
final long tid = getThreadId(Thread.currentThread());
}
readHolds:继承了 ThreadLocal 的类
static final class ThreadLocalHoldCounter
extends ThreadLocal<HoldCounter> {
// 这里定义了 initialValue
// ThreadLocal 第一次初始化 map 会调用这个函数
public HoldCounter initialValue() {return new HoldCounter();
}
}
总结: 就是把所有获取读锁的线程 + 次数 都存着
至于为什么这样存,我不明确作者的心理,或者跟过后 HashMap 为什么用头插法一样,这样记录在开释锁的时候更快?不知道了 …
3. 看一下读锁的开释过程
ReadWriteLock rw = new ReentrantReadWriteLock();
rw.readLock().unlock();
ReentrantReadWriteLock#tryReleaseShared:
// AQS#releaseShared
public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {
// 如果返回 true 也就是开释完锁后 以后线程的读锁曾经没有了
// 没有的话 做一个 doReleaseShared 操作 能够简略了解为唤醒其余期待线程(队列里下一个期待的线程)doReleaseShared();
return true;
}
return false;
}
protected final boolean tryReleaseShared(int unused) {
// 获取以后线程
Thread current = Thread.currentThread();
// 查看 firstReader 是不是以后线程 firstReader 在这里用到了
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
// 判断 firstReaderHoldCount 如果等于 1 把 firstReader 置空
// 也就是获取过一次锁
if (firstReaderHoldCount == 1)
firstReader = null;
else
// 否则 就 减 1
firstReaderHoldCount--;
} else {
// 如果不是首个获取读锁的线程
HoldCounter rh = cachedHoldCounter;
// 判断 cachedHoldCounter 是不是以后线程的持有器 如果不是那就从 readHolds(ThreadLocal)中获取
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
// 判断持有锁次数
int count = rh.count;
// <=1 移除 ThreadLocal 变量 记着 ThreadLocal 最初肯定要 remove 否则就是内存透露
if (count <= 1) {readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();}
// 如果持有次数很多 那就减 1
--rh.count;
}
//
for (;;) {
// state-SHARED_UNIT
// SHARED_UNIT = (1 << SHARED_SHIFT) = 65536
int c = getState();
int nextc = c - SHARED_UNIT;
// cas 设置新值
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;
}
}
//
private void doReleaseShared() {
//
for (;;) {
Node h = head;
// h != null && h != tail 如果为 true 示意队列中有期待线程
if (h != null && h != tail) {
// 获取 waitStatus waitStatus 状态在之前文章中有提到过 是他后继线程给他的状态
int ws = h.waitStatus;
// 如果状态是 SIGNAL 须要唤醒
if (ws == Node.SIGNAL) {if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
// 唤醒
unparkSuccessor(h);
}
// 如果状态是 0 就是不须要唤醒 设置为 PROPAGATE 状态 接着往后唤醒须要唤醒的人
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // 如果 cas 失败了 接着循环 loop on failed CAS
}
// 如果 head 变换了 须要接着循环
if (h == head) // loop if head changed
break;
}
}
4. 留神
写这些常识为了抛砖,也不是很欠缺,其实能够本人写一个办法 跟着断点,进源码看看 那样更容易加深印象。
对于写锁的获取与开释 读者敌人自行钻研吧 有问题能够给我留言 大家一起探讨哈
有问题留言哦,或者关注公众号(回复快):
正文完