(juc系列)ReentrantReadWriteLock源码学习

本文源码基于: JDK13

简介

这个类是一个ReadWriteLock的实现类,实现了类似于ReentrantLock的语义.

这个类有以下特性:

获取顺序

这个类没有给读写者强加获取锁的顺序,但是他实现了一个可选的公平策略。

  • 非公平模式(默认模式
    当创建一个非公平的锁,获取读锁,写锁的顺序是没有指定的. 满足可重入性的约束.

一个非公平锁,可能会因为不断的争执,而无限期的推迟一个或者多个读锁/写锁的获取线程,但是通常来讲拥有更好的吞吐量.

  • 公平模式

当创建一个公平所,线程竞争使用一个到达序的策略. 当前持有锁的线程释放锁,等待时间最长的单个写入线程就拿到写锁, 或者如果有一组读线程,
等待的时间比所有的写线程都长,那么这组读线程将拿到读锁.

如果当前锁正在被写锁持有,或者有一个等待的写线程,公平模式下的获取读锁的请求将会被阻塞. 直到等待时间最长的写线程拿到锁并释放.

当然,如果一个等待中的写线程放弃了,让一个或者多少读线程成为了队列中等待最久的, 这些读线程将拿到读锁.

如果一个写线程尝试获取锁,除非当前所有的读锁和写锁都是空闲的, 才能拿到锁,意味着当前不能有任何的等待线程.

可重入性

这个锁允许所有的读线程和写线程重复的申请对应的锁,就像ReentrantLock一样. 不是重入的读线程将被正在持有锁的写线程阻塞.

一个写线程可以获取读锁. 在很多应用中,可重入性很有用,当写线程持有写锁,在某些调用或者回调方法中执行读操作。如果一个读线程尝试去申请写锁,永远不会成功.

锁降级

支持从写锁降级到读锁,但是从读锁升级到写锁是不允许的.

支持Condition

写锁提供了一个Condition的实现,他的行为模式和写锁一样. 就像ReentrantLock中的Condition一样.读锁不支持Condition.

仪表盘

这个类支持查看锁被持有还是竞争中,这些方法用于监视系统状态,而不是用于同步控制.

这个锁的序列化和内置锁的行为方式相同,反序列化的锁处于解锁状态,无论序列化时状态如何.

简单的使用案例.

代码片段简单的展示了在更新cache之后如何进行锁的降级.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class CachedData {
Object data;
boolean cacheValid;
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

void processCachedData() {
// 读锁
rwl.readLock().lock();

if (!cacheValid) {
// Must release read lock before acquiring write lock
// 释放读锁
rwl.readLock().unlock();
// 申请写锁
rwl.writeLock().lock();
try {
// Recheck state because another thread might have
// acquired write lock and changed state before we did.
if (!cacheValid) {
// 新数据的赋值
data = ...
cacheValid = true;
}
// Downgrade by acquiring read lock before releasing write lock
// 降级成读锁
rwl.readLock().lock();
} finally {
// 释放写锁,还持有读锁
rwl.writeLock().unlock(); // Unlock write, still hold read
}
}

try {
use(data);
} finally {
rwl.readLock().unlock();
}
}
}
  1. 首先获取读锁.
  2. 释放读锁,同时申请写锁.
  3. 完成写操作后,申请读锁.
  4. 释放写锁,持有读锁
  5. 完全使用完成后,释放读锁。

ReentrantReadWriteLock可以用在一些集合类中,用来提升并发性. 只有当集合预期很大,且被很多歌读线程访问,数量远多余写线程时是值得的.

下面是一个使用TreeMap的类,预期很大且会有并发的访问.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class RWDictionary {
private final Map<String, Data> m = new TreeMap<>();
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();

public Data get(String key) {
r.lock();
try { return m.get(key); }
finally { r.unlock(); }
}
public List<String> allKeys() {
r.lock();
try { return new ArrayList<>(m.keySet()); }
finally { r.unlock(); }
}
public Data put(String key, Data value) {
w.lock();
try { return m.put(key, value); }
finally { w.unlock(); }
}
public void clear() {
w.lock();
try { m.clear(); }
finally { w.unlock(); }
}
}

这个类对TreeMap进行了封装,使用TreeMap+ReentrantReadWriteLock实现了一个线程安全的TreeMap.

这个类支持最大65535个重入的写入所和65535个读锁.超过这个限制,会返回Error.

源码阅读

这个类使用AQS框架实现,先来看一下AQS的子类Sync.

Sync

变量

首先是几个属性.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/*
* Read vs write count extraction constants and functions.
* Lock state is logically divided into two unsigned shorts:
* The lower one representing the exclusive (writer) lock hold count,
* and the upper the shared (reader) hold count.
*/

// 共享锁的位数
static final int SHARED_SHIFT = 16;
// 共享锁unit
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
// 最大数量
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
// 独占锁的mask.
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;

/**
* A counter for per-thread read hold counts.
* Maintained as a ThreadLocal; cached in cachedHoldCounter.
*/
// 每个线程持有的读锁的数量
static final class HoldCounter {
int count; // initially 0
// Use id, not reference, to avoid garbage retention
final long tid = LockSupport.getThreadId(Thread.currentThread());
}

/**
* ThreadLocal subclass. Easiest to explicitly define for sake
* of deserialization mechanics.
*/
// 用ThreadLocal记录每个线程持有的读锁的数量
static final class ThreadLocalHoldCounter
extends ThreadLocal<HoldCounter> {
public HoldCounter initialValue() {
return new HoldCounter();
}
}

/**
* The number of reentrant read locks held by current thread.
* Initialized only in constructor and readObject.
* Removed whenever a thread's read hold count drops to 0.
*/
// 当前线程的读锁持有数量
private transient ThreadLocalHoldCounter readHolds;

/**
* The hold count of the last thread to successfully acquire
* readLock. This saves ThreadLocal lookup in the common case
* where the next thread to release is the last one to
* acquire. This is non-volatile since it is just used
* as a heuristic, and would be great for threads to cache.
*
* <p>Can outlive the Thread for which it is caching the read
* hold count, but avoids garbage retention by not retaining a
* reference to the Thread.
*
* <p>Accessed via a benign data race; relies on the memory
* model's final field and out-of-thin-air guarantees.
*/
// 上一个成功获取读锁的线程持有的数量
private transient HoldCounter cachedHoldCounter;

/**
* firstReader is the first thread to have acquired the read lock.
* firstReaderHoldCount is firstReader's hold count.
*
* <p>More precisely, firstReader is the unique thread that last
* changed the shared count from 0 to 1, and has not released the
* read lock since then; null if there is no such thread.
*
* <p>Cannot cause garbage retention unless the thread terminated
* without relinquishing its read locks, since tryReleaseShared
* sets it to null.
*
* <p>Accessed via a benign data race; relies on the memory
* model's out-of-thin-air guarantees for references.
*
* <p>This allows tracking of read holds for uncontended read
* locks to be very cheap.
*/
// 第一个申请读锁的线程
// 第一个申请读锁的线程,现在持有的读锁数量.
private transient Thread firstReader;
private transient int firstReaderHoldCount;

首先定义了一些常量,用来指示在State状态的定义中,读写锁的表示方法等. 以及内部的读锁计数的保存》

构造方法

1
2
3
4
5

Sync() {
readHolds = new ThreadLocalHoldCounter();
setState(getState()); // ensures visibility of readHolds
}

比较简单,初始化了一个当前线程的计数器,然后检查了一下初始状态.

tryRelease

这是AQS中释放独占锁的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
* Note that tryRelease and tryAcquire can be called by
* Conditions. So it is possible that their arguments contain
* both read and write holds that are all released during a
* condition wait and re-established in tryAcquire.
*/
@ReservedStackAccess
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;
}

首先判断是否独占锁,不是的话抛出异常.

  1. 用当前State减去要释放的数量.
  2. 如果释放后,独占锁的数量为0. 则锁释放成功.将锁的当前线程设置为null.
  3. 如果独占锁的数量仍不为0(可重入锁),则释放返回仍未释放.

tryAcquire

这是AQS中获取独占锁的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@ReservedStackAccess
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);
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;
}

  1. 首先拿到当前的线程以及当前锁的State.
  2. 如果锁的状态不为0, 意味着当前有锁被持有. 但是独占锁的数量为0. 意味着当前锁在被shared模式持有. 直接返回加锁失败.
  3. 对锁的状态递增此次申请的数量. 如果超过最大数量,抛出异常. 未超过,设置状态. 加锁成功.
  4. 如果锁的状态为0. 且当前写锁应该被阻塞,或者设置状态尝试获取锁失败,都返回加锁失败. 否则加锁成功,设置当前持有锁的线程.

tryReleaseShared 释放共享锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@ReservedStackAccess
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
// 当前线程是第一个读线程
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
// 拿到缓存的holder.或者当前线程的holder.
HoldCounter rh = cachedHoldCounter;
if (rh == null ||
rh.tid != LockSupport.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;
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;
}
}

这是AQS中释放共享锁的操作:

  1. 如果当前线程是第一个获取读锁的线程: 将当前类持有的firstReaderfirstReaderHoldCount进行相应的赋值.递减/置为null.
  2. 拿到上一个持有共享锁的读线程,如果当前线程不是上一个线程. 就拿到当前线程的holder.
  3. 对拿到的线程持有数量的holder进行递减.
  4. 对共享锁进行递减,注意: 共享锁使用的是State的高位部分, 因此每次减去的值是: SHARED_UNIT.
  5. 设置状态成功,返回释放后的state是否为0.

tryAcquireShared 获取共享锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@ReservedStackAccess
protected final int tryAcquireShared(int unused) {
/*
* Walkthrough:
* 1. If write lock held by another thread, fail.
* 2. Otherwise, this thread is eligible for
* lock wrt state, so ask if it should block
* because of queue policy. If not, try
* to grant by CASing state and updating count.
* Note that step does not check for reentrant
* acquires, which is postponed to full version
* to avoid having to check hold count in
* the more typical non-reentrant case.
* 3. If step 2 fails either because thread
* apparently not eligible or CAS fails or count
* saturated, chain to version with full retry loop.
*/
Thread current = Thread.currentThread();
// 当前状态
int c = getState();
// 独占锁被持有,并且独占的线程不是当前线程,直接获取失败失败
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
// 当前持有的共享锁的数量
int r = sharedCount(c);
// 获取共享锁是否应该被阻塞&&共享锁数量小于最大值&&递增状态State成功.
// 意味着加锁成功了.
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
// r==0意味着当前没有共享锁,那么当前线程就是第一个读线程,进行赋值
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;

} else if (firstReader == current) {
// 当前线程已经是第一个读线程了,对相关参数进行递增
firstReaderHoldCount++;
} else {
// 获取上一个读锁的获取者
HoldCounter rh = cachedHoldCounter;
if (rh == null ||
rh.tid != LockSupport.getThreadId(current))
// 如果线程不是上一个线程,或者上一个缓存的为空
// 将当前的线程缓存起来
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
// 当前线程就是缓存的上一个线程,但是数量为0. 就设置为readHold
readHolds.set(rh);
// 获取的读锁数量+1.
rh.count++;
}
// 代表成功了.
return 1;
}
return fullTryAcquireShared(current);
}

获取共享锁:整体的流程如备注中所述,上面的方法处理了:

  1. 当前获取不阻塞.
  2. 共享锁数量未超过最大值
  3. CAS能成功.

这三个条件均满足的情况,如果不满足,调用了fullTryAcquireShared来处理.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

/**
* Full version of acquire for reads, that handles CAS misses
* and reentrant reads not dealt with in tryAcquireShared.
*/
final int fullTryAcquireShared(Thread current) {
/*
* This code is in part redundant with that in
* tryAcquireShared but is simpler overall by not
* complicating tryAcquireShared with interactions between
* retries and lazily reading hold counts.
*/
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 != LockSupport.getThreadId(current)) {
// 缓存的不是当前线程,取当前线程的.
rh = readHolds.get();
// 持有锁数量为0. 删掉
if (rh.count == 0)
readHolds.remove();
}
}
// 没看懂,为啥缓存的或者当前的为0. 要返回-1
if (rh.count == 0)
return -1;
}
}
// 当前读锁数量到达最大了,抛出异常
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// 如果设置+1个读锁成功
if (compareAndSetState(c, c + SHARED_UNIT)) {
// 如果读锁为0.那么当前线程就是第一个读锁
if (sharedCount(c) == 0) {
firstReader = current;
firstReaderHoldCount = 1;
// 第一个读锁重入
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
// 缓存上一个获取读锁的线程
if (rh == null)
rh = cachedHoldCounter;
if (rh == null ||
rh.tid != LockSupport.getThreadId(current))
rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
cachedHoldCounter = rh; // cache for release
}
// 成功
return 1;
}
// 如果CAS设置状态失败,继续自旋
}
}

其实和tryAcquireShared很像,只是通过分离代码处理了额外的几种情况.

tryWriteLock 获取写锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

/**
* Performs tryLock for write, enabling barging in both modes.
* This is identical in effect to tryAcquire except for lack
* of calls to writerShouldBlock.
*/
@ReservedStackAccess
final boolean tryWriteLock() {
Thread current = Thread.currentThread();
int c = getState();
if (c != 0) {
int w = exclusiveCount(c);
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
}
if (!compareAndSetState(c, c + 1))
return false;
setExclusiveOwnerThread(current);
return true;
}

tryAcquire效果一样,只是不考虑writerShouldBlock.

tryReadLock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

/**
* Performs tryLock for read, enabling barging in both modes.
* This is identical in effect to tryAcquireShared except for
* lack of calls to readerShouldBlock.
*/
@ReservedStackAccess
final boolean tryReadLock() {
Thread current = Thread.currentThread();
for (;;) {
int c = getState();
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return false;
int r = sharedCount(c);
if (r == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null ||
rh.tid != LockSupport.getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return true;
}
}
}

tryAcquireShared效果一样,只是不考虑readerShouldBlock.

NonfairSync 非公平状态下的Sync

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

/**
* Nonfair version of Sync
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -8159625535654395037L;
final boolean writerShouldBlock() {
return false; // writers can always barge
}
final boolean readerShouldBlock() {
/* As a heuristic to avoid indefinite writer starvation,
* block if the thread that momentarily appears to be head
* of queue, if one exists, is a waiting writer. This is
* only a probabilistic effect since a new reader will not
* block if there is a waiting writer behind other enabled
* readers that have not yet drained from the queue.
*/
return apparentlyFirstQueuedIsExclusive();
}
}

主要是定义了父类中的两个抽象方法.

  • writerShouldBlock. 写锁的请求,任何时候都可以申请.
  • readerShouldBlock . 读锁的请求,能不能申请,要看情况咯.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

/**
* Returns {@code true} if the apparent first queued thread, if one
* exists, is waiting in exclusive mode. If this method returns
* {@code true}, and the current thread is attempting to acquire in
* shared mode (that is, this method is invoked from {@link
* #tryAcquireShared}) then it is guaranteed that the current thread
* is not the first queued thread. Used only as a heuristic in
* ReentrantReadWriteLock.
*/
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
return (h = head) != null &&
(s = h.next) != null &&
!s.isShared() &&
s.thread != null;
}

这是为了避免因为非公平的竞争,而把写锁饿死的情况实现的一个方法:

如果当前等待队列中有两个节点,且第二个还是独占的写锁等待,当前的读锁请求就不允许提交了.

这是一个概率上的问题,如果等待队列里面已经有一个写锁排在读锁后面了,害怕把写锁饿死,就在这里不让别的读锁来竞争,知道前面的锁走完.

FairSync 公平模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14

/**
* Fair version of Sync
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -2274990926593161451L;
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
final boolean readerShouldBlock() {
return hasQueuedPredecessors();
}
}

公平锁,对于读写锁是公平的,都是看队列中有没有已经在等待的节点了.(这部分是在AQS实现的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public final boolean hasQueuedPredecessors() {
Node h, s;
if ((h = head) != null) {
if ((s = h.next) == null || s.waitStatus > 0) {
s = null; // traverse in case of concurrent cancellation
for (Node p = tail; p != h && p != null; p = p.prev) {
if (p.waitStatus <= 0)
s = p;
}
}
// 队列中第一个在等待的节点,不是当前节点,那么当前线程就不要提交了
if (s != null && s.thread != Thread.currentThread())
return true;
}
// 当前节点的请求可以提交.
return false;
}

ReadLock

read是一个实现了Lock接口的子类. 持有一个Sync同步器.

lock

1
2
3
4
public void lock() {
sync.acquireShared(1);
}

读锁的加锁,调用了AQS的申请一个共享锁.

tryLock

1
2
3
4
public boolean tryLock() {
return sync.tryReadLock();
}

读锁的尝试加锁. 调用的是同步器的tryReadLock,也就是不考虑readerShouldBlock,而强行进行的一次加锁行为.

unlock

1
2
3
4
public void unlock() {
sync.releaseShared(1);
}

读锁的解锁,是调用AQS的释放一次共享锁.

WriteLock

写锁的实现,也是实现Lock接口的一个实现类.

lock

1
2
3
4
public void lock() {
sync.acquire(1);
}

写锁的加锁,是调用AQS的获取一个独占锁实现.

tryLock

1
2
3
4
public boolean tryLock() {
return sync.tryWriteLock();
}

写锁的tryLock.调用sync.tryWriteLock.不考虑writerShoulBlock进行的一次强行尝试.

unlock

1
2
3
4
public void unlock() {
sync.release(1);
}

写锁的解锁,调用AQS的释放独占锁一次.

其他

其他还有一些对于类内部属性的查询方法,主要用于对当前锁状态的监控,这里就不展开了. 都是比较简单的属性查询.

总结

ReentrantReadWriteLock, 继承自AQS框架, 实现了读写锁,可重入特性.

既然是继承自AQS框架. 那么主要工作仍然是对State的定义.

  1. State的低16位,保存独占锁的加锁信息及重入次数.
  2. State的高16位,保存共享锁的加锁信息,及加锁数量. 共享锁的重入次数,由单独的属性进行保存.
  • 读写锁

读写锁功能由两个子类ReadLockWriteLock实现,负责调用AQS的独占锁加解锁和共享锁的加解锁.

  • 公平锁/非公平锁

由AQS的子类SyncNonfairSync``FairSync实现,主要是控制新加入的加锁请求是否要排队来实现的.

  • 可重入性

可重入锁,需要记录锁的持有线程,对当前线程持有的数量进行递增递减,而不是简单的是否为某个特定值.

写锁的重入数量保存在State的低16位. 读锁的重入数量保存在readHolds中. 由一个类似于ThreadLocal的结构进行保存线程->重入数量的对应关系.

  • 为什么需要单独保存第一个读锁的线程? firstReaderfirstReaderHoldCount.

总的说是为了减少ThreadLocal的数量,减少内存占用。详细分析看这里: https://www.mdnice.com/writing/f2beb7d9c58f43afbdeec9ce708d4582

参考文章

关于ReentrantReadWriteLock,第一个获取读锁的线程单独记录问题讨论(firstReader和firstReaderHoldCount)


完。





联系我

最后,欢迎关注我的个人公众号【 呼延十 】,会不定期更新很多后端工程师的学习笔记。
也欢迎直接公众号私信或者邮箱联系我,一定知无不言,言无不尽。



以上皆为个人所思所得,如有错误欢迎评论区指正。

欢迎转载,烦请署名并保留原文链接。

联系邮箱:huyanshi2580@gmail.com

更多学习笔记见个人博客或关注微信公众号 <呼延十 >——>呼延十