源码篇 预防针: 源码篇 必然会有很多代码,期望大家不要有畏难情绪,虽然我也是看到一大串代码就头疼。 我贴代码仅仅为了便利我的文字解答,源码仅仅辅佐我文字讲解,所以大家尽量重视我的文字就好啦。
模版办法
好了到这儿就要开端进入主题了,依照之前的过程,咱们要开端从 AQS
的模版办法开端介绍 ReentrantReadWriteLock
。
WriteLock:写锁
public static class WriteLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = -4992448646407690164L;
private final Sync sync;
protected WriteLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
/** 调用父类的 acquire 办法 */
public void lock() {
sync.acquire(1);
}
}
acquire:写锁的模版办法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
这儿再贴一下 AQS
的模版办法,然后咱们需求重视的就是 ReentrantReadWriteLock
实现的 tryAcquire
办法。
tryAcquire:写锁的加锁逻辑
WriteLock
中心的加锁逻辑。
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();
int c = getState();
int w = exclusiveCount(c);
// c != 0 代表着我并不是初次来获取锁,之前就现已存在了读写锁了
// 假如我是初次来获取锁的线程,我能够很高兴的直接经过下面的 cas 去进行加锁
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;
}
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
假如你把源码上面的注开释到翻译软件上面看,就会得到这坨东西:
不知道对你有没有协助,反正对我是没有一点协助。
关于 tryAcquire
相信你看过我之前对 ReentrantLock
的讲解应该基本是没问题,需求特别注意的只有他多了一个 writerShouldBlock
办法,而这个办法就是 写锁公正性 的表现。
writerShouldBlock:写锁的公正性表现
咱们再来看看 writerShouldBlock
这个办法
/** 公正锁 */
static final class FairSync extends Sync {
private static final long serialVersionUID = -2274990926593161451L;
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
}
/** 非公正锁 */
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -8159625535654395037L;
final boolean writerShouldBlock() {
return false; // writers can always barge
}
}
-
公正锁:就很简单,直接让交给
CLH
锁去处理,关于CLH
锁的介绍在上一篇文章有解释:TODO:。 -
非公正锁:是直接返回
False
,一直答应插队, 这也是 排他锁 的特性表现。
这边贴一个我用更直白的话语注释好的,便利读者了解具体的过程
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
// c != 0 代表着我并不是初次来获取锁,之前就现已存在了读锁或写锁了
// 假如我是初次来获取锁的线程,我能够很高兴的直接经过下面的 cas 去进行加锁
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");
// 都满足了合法性检查,那么就能够直接更新 state 代表获取锁成功了
// Reentrant acquire
setState(c + acquires);
return true;
}
// 来到这儿还需求判别 公正性
// 公正锁:假如有前置节点,代表需求排队,就交给 CLH 去排队
// 非公正锁:总是答应插队,直接经过 CAS 插队
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
tryRelease:写锁的开释锁逻辑
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
int nextc = getState() - releases;
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}
先判别是否是 锁持有线程,然后再依据 锁重入次数 判别 是否完好开释锁。
写锁获取锁流程
- 判别是否是 之前是否有持有锁 来获取写锁:
- 假如是:进行 合法性判别:
- 排他性判别(假如现已有同享锁,则会失利,由于排他性质)。
- 可重入性判别。
- 最大次数判别。
- 假如不是:则还需求依据 公正性 进行
CAS
获取。-
公正锁:就扔去
CLH
行列排队获取 -
非公正锁:直接插队进行
CAS
获取锁,不行再丢去CLH
行列排队。
-
公正锁:就扔去
- 假如是:进行 合法性判别:
总结
-
写锁的互斥性:在获取锁的时分,会先依据
state
判别是否持有锁,这儿包含了 排他性 的判别,由于写锁和所有锁都互斥,所以只需求判别state ≠ 0
,代表这儿发生互斥了而且假如不是锁持有的线程,那么就会获取锁失利。 -
写锁的公正性 :表现在
writerShouldBlock
这个办法中,公正锁 直接丢CLH
行列乖乖排队,非公正锁 能够先插队进行CAS
尝试获取锁,获取失利再丢去CLH
。
来都来了,点个赞,留个言再走吧彦祖,这对我来说非常重要!