整体概念
- ReentrantReadWriteLoc提供读写锁机制,读锁共享,写锁互斥,读写分离
- 写锁可以降级为读锁
- 支持公平锁和非公平锁
核心内部类分析
Sync
Sync继承AQS实现锁的逻辑,有关AQS的原理参考juejin.cn/post/748158... ,本文涉及AQS的方法不再重复分析。
HoldCounter
arduino
static final class HoldCounter {
//记录线程的读锁重入次数
int count = 0;
// Use id, not reference, to avoid garbage retention
final long tid = getThreadId(Thread.currentThread());
}
ThreadLocalHoldCounter
ThreadLocal的子类,表示读锁线程的HoldCounter
csharp
static final class ThreadLocalHoldCounter
extends ThreadLocal<HoldCounter> {
public HoldCounter initialValue() {
return new HoldCounter();
}
}
属性字段
arduino
//读锁偏移量
static final int SHARED_SHIFT = 16;
//读锁偏移单位
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
//最大重入次数
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
//掩码
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
arduino
//获取高16位读锁的数量
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
//获取低16位写锁的数量
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
构造方法
scss
Sync() {
//初始读锁的线程持有数量
readHolds = new ThreadLocalHoldCounter();
//确保readHolds的可见性,防止其他线程获取未初始化完成的readHolds对象
// volatile的写操作会立即刷新到主内存,并防止指令重排序,同时还会强制其之前的所有普通写操作也刷新到主内存,所以这里也保证了其他线程看到初始化完成的readHolds
setState(getState()); // ensures visibility of readHolds
}
NonfairSync 非公平锁
writerShouldBlock()
arduino
//写操作是否需要阻止
final boolean writerShouldBlock() {
// 不阻止 允许新来的写锁线程和队列中的线程竞争(允许插队)
return false; // writers can always barge
}
arduino
//读操作是否需要阻止
final boolean readerShouldBlock() {
//看队列的第一个有效节点是否是写锁线程
return apparentlyFirstQueuedIsExclusive();
}
java
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
return
//队头不为空(哨兵节点)
(h = head) != null &&
//第一个有效节点
(s = h.next) != null &&
//是否是共享模式
!s.isShared() &&
//是否是有效线程
s.thread != null;
}
FairSync 公平锁
arduino
//写操作是否需要阻止
final boolean writerShouldBlock() {
//同步队列中有有效节点在排队,公平锁则不插队去枪锁
return hasQueuedPredecessors();
}
java
public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return
//h==t的话说明队列是空的或者队列仅有一个无效等待的节点
//h!=t说明同步队列中存在有效节点在排队
h != t &&
//说明头节点已有值(end()方法入队时候pre已经设置了值,但tail还没来得及设置,这时候认为有节点在排队)
((s = h.next) == null ||
//不等于当前线程,说明有其他节点在排队
s.thread != Thread.currentThread());
}
arduino
//读操作是否需要阻止
final boolean readerShouldBlock() {
return hasQueuedPredecessors();
}
构造方法
csharp
public ReentrantReadWriteLock() {
this(false);
}
arduino
public ReentrantReadWriteLock(boolean fair) {
//默认非公平锁
sync = fair ? new FairSync() : new NonfairSync();
//构造读锁 非公平
readerLock = new ReadLock(this);
//构造写锁 非公平
writerLock = new WriteLock(this);
}
加锁
写锁加锁
csharp
public void lock() {
sync.acquire(1);
}
scss
public final void acquire(int arg) {
if (
//尝试获取锁
!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
java
protected final boolean tryAcquire(int acquires) {
//当前线程
Thread current = Thread.currentThread();
//状态值
int c = getState();
//写锁数量
int w = exclusiveCount(c);
//说明有读锁或者是写锁
if (c != 0) {
//这段源码注释的意思是存在读锁,那么写锁加锁直接失败,因为读锁和写锁是互斥的
// (Note: if c != 0 and w == 0 then shared count != 0)
if (
//写锁的数量是0,加上c!=0(存在读锁或者是写锁)的条件,即从c!= && w==0 说明是读锁持有,那么写锁加锁肯定失败的,因为读锁和写锁是互斥的
w == 0 ||
//不存在读锁的话(写锁状态),不是当前线程独占,说明有别的写锁线程还在临界区执行代码没有释放锁,加锁失败,写锁和写锁也是互斥的
current != getExclusiveOwnerThread())
return false;
//超过最大数量抛异常
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
//到这里说明是写锁自己加锁,没有别的写锁和读锁干扰,设置state即可
setState(c + acquires);
//写锁加锁成功
return true;
}
//是否需要阻止加锁
//这里是非公平锁,允许和同步队列中的线程枪锁
if (writerShouldBlock() ||
//CASstate加1
!compareAndSetState(c, c + acquires
//加锁失败
return false;
//成功则设置独占标识
setExclusiveOwnerThread(current);
return true;
}
读锁加锁
csharp
public void lock() {
sync.acquireShared(1);
}
arduino
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
//加锁失败加入同步队列
doAcquireShared(arg);
}
ini
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
if (
//写锁的数量不为0,说明有写锁加锁过还没有释放完
exclusiveCount(c) != 0 &&
//不是当前线程,说明也不是当前线程持有的写锁,若是当前线程持有的写锁是允许读锁加锁的(锁的降级)
getExclusiveOwnerThread() != current)
return -1;
//读锁数量
int r = sharedCount(c);
if (
//读锁是否需要被阻止
//队列的第一个有效节点是写锁,读锁不去竞争锁
//防止读锁一直抢到锁导致写锁得不到释放造成写锁饥饿
!readerShouldBlock() &&
r < MAX_COUNT &&
//CAS读锁数量
compareAndSetState(c, c + SHARED_UNIT)) {
//读锁数量为0
if (r == 0) {
//当前线程第一个获取读锁
firstReader = current;
//持有次所为1
firstReaderHoldCount = 1;
}
//第一个获取读锁的线程再次加锁
else if (firstReader == current) {
//持有次数加1
firstReaderHoldCount++;
} else {
//最后一次读锁线程持有数量的缓存
HoldCounter rh = cachedHoldCounter;
//为空或者不是当前线程
if (rh == null || rh.tid != getThreadId(current))
//更新缓存
cachedHoldCounter = rh = readHolds.get();
//计数为0,设置缓存
else if (rh.count == 0)
readHolds.set(rh);
//当前线程计数加1
rh.count++;
}
return 1;
}
//循环加锁
return fullTryAcquireShared(current);
}
csharp
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
for (;;) {
int c = getState();
//写锁存在
if (exclusiveCount(c) != 0) {
//不是当前线程持有的写锁,读锁和写锁互斥加锁失败
if (getExclusiveOwnerThread() != current)
return -1;
// else we hold the exclusive lock; blocking here
// would cause deadlock.
} 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)) {
//设置当前线程readHolds
rh = readHolds.get();
//当前线程数锁记录为0
if (rh.count == 0)
//清除无效的锁记录
readHolds.remove();
}
}
//不是读锁重入,状态为0加锁失败
if (rh.count == 0)
return -1;
}
}
//校验读锁的数量
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
//设置读锁状态
if (compareAndSetState(c, c + SHARED_UNIT)) {
//读锁数量是0
if (sharedCount(c) == 0) {
//设置firstReader
firstReader = current;
//设置firstReaderHoldCount
firstReaderHoldCount = 1;
}
//读锁重入
else if (firstReader == current) {
//计数加1
firstReaderHoldCount++;
} else {
if (rh == null)
//为空更新
rh = cachedHoldCounter;
//为空或者不是当前线程
if (rh == null || rh.tid != getThreadId(current))
//从ThreadLocal中更新
rh = readHolds.get();
//计数为0
else if (rh.count == 0)
//重新设置
readHolds.set(rh);
//计数加1
rh.count++;
//更新缓存
cachedHoldCounter = rh; // cache for release
}
//加锁成功
return 1;
}
}
}
解锁
写锁解锁
csharp
public void unlock() {
sync.release(1);
}
java
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//唤醒后继线程
unparkSuccessor(h);
return true;
}
return false;
}
scss
protected final boolean tryRelease(int releases) {
//不是独占报错
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//状态减1
int nextc = getState() - releases;
//卸货状态是否为0
boolean free = exclusiveCount(nextc) == 0;
//是的话
if (free)
//清空独占线程标识
setExclusiveOwnerThread(null);
//设置状态值
setState(nextc);
return free;
}
读锁解锁
csharp
public void unlock() {
sync.releaseShared(1);
}
arduino
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
//解锁成功唤醒后继所有节点
doReleaseShared();
return true;
}
return false;
}
scss
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
//当前线程是firstReader
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
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;
//小于等于1
if (count <= 1) {
//清除
readHolds.remove();
//校验重复释放
if (count <= 0)
throw unmatchedUnlockException();
}
//加锁次数减1
--rh.count;
}
for (;;) {
//状态值
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.
//高16位为0说明读锁释放了
return nextc == 0;
}
}