Java源码分析系列笔记-17.ReentrantReadWriteLock
1. ReentrantReadWriteLock是什么
ReentrantLock保证了同一时间只有一个线程可以在临界区读或者写数据,这意味着如果有两个读线程同时读取数据,ReentrantLock也只允许其中一个通过,但我们想要的是读可以并发执行,一旦有写则其他线程等待。如下表:
| 是否可以同时进行 | 读 | 写 |
|---|---|---|
| 读 | √ | × |
| 写 | × | × |
因此,ReentrantReadWriteLock就诞生了
2. 非公平ReentrantReadWriteLock
2.1. 是什么
无论队列前面是否有人排队等待锁,我直接去抢
2.2. 怎么使用
public class ReadWriteLockTest
{
private static ReadWriteLock lock = new ReentrantReadWriteLock();//默认非公平
private static Lock readLock = lock.readLock();
private static Lock writeLock = lock.writeLock();
private static List<Integer> data = new ArrayList<>();
public static void main(String[] args) throws InterruptedException
{
Thread readThread = new Thread(() -> {
while (true)
{
try
{
TimeUnit.MILLISECONDS.sleep(500);
readLock.lock();
System.out.println(Thread.currentThread().getName() + " read: " + data);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
readLock.unlock();
}
}
});
Thread readThread2 = new Thread(() -> {
while (true)
{
try
{
TimeUnit.MILLISECONDS.sleep(300);
readLock.lock();
System.out.println(Thread.currentThread().getName() + " read: " + data);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
readLock.unlock();
}
}
});
Thread writeThread = new Thread(() -> {
int i = 0;
while (true)
{
try
{
TimeUnit.MILLISECONDS.sleep(200);
writeLock.lock();
if (i % 2 == 0)
{
data.add(i);
}else
{
data.remove(0);
}
i++;
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
writeLock.unlock();
}
}
});
readThread.start();
readThread2.start();
writeThread.start();
readThread.join();
readThread2.join();
writeThread.join();
}
}
2.3. 源码分析
2.3.1. uml
@startuml
skinparam classAttributeIconSize 0
interface AQS{
}
class Sync{
}
interface Lock{
}
interface ReadWriteLock{
}
class ReentrantReadWriteLock{
}
class WriteLock{
}
class ReadLock{
}
class WriteLock{
}
Lock <|-- ReadLock
Lock <|-- WriteLock
ReadWriteLock <|-- ReentrantReadWriteLock
AQS <|-- Sync
ReentrantReadWriteLock --> ReadLock
ReentrantReadWriteLock --> WriteLock
ReadLock --> Sync
WriteLock --> Sync
@enduml
2.3.2. 构造方法
- ReentrantReadWriteLock
public ReentrantReadWriteLock() {
//默认是false
this(false);
}
public ReentrantReadWriteLock(boolean fair) {
//初始化了Sync
//false的话使用的是NonfairSync
sync = fair ? new FairSync() : new NonfairSync();
//初始化读写锁
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
- ReentrantReadWriteLock.ReadLock
protected ReadLock(ReentrantReadWriteLock lock) {
//其实就是保存了ReentrantReadWriteLock的Sync
sync = lock.sync;
}
- ReentrantReadWriteLock.WriteLock
protected WriteLock(ReentrantReadWriteLock lock) {
//其实就是保存了ReentrantReadWriteLock的Sync
sync = lock.sync;
}
2.3.3. 读锁加锁
- ReentrantReadWriteLock.readLock
//返回就是读锁
public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }
- ReadLock.lock
public void lock() {
//使用AQS加共享锁
sync.acquireShared(1);
}
2.3.3.1. 使用AQS加共享锁
- AQS acquireShared
public final void acquireShared(int arg) {
//ReentrantReadWriteLock的Sync重写了tryAcquireShared
//所以这里调用的是Sync的tryAcquireShared
//如果返回<0说明尝试加锁失败,执行doAcquireShared入AQS队列并阻塞,等待唤醒
//返回>=0说明加锁成功,执行后续的业务逻辑
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
2.3.3.1.1. 使用Sync尝试加共享锁
- ReentrantReadWriteLock.Sync.tryAcquireShared
protected final int tryAcquireShared(int unused) {
//当前线程
Thread current = Thread.currentThread();
//当前的state数目(或者说加锁【既包括读锁也包括写锁】的数量)
int c = getState();
//从state中获取互斥锁的数目(写锁的数量)
//如果数量不为0(即>0)说明已经加了写锁
if (exclusiveCount(c) != 0 &&
//判断加锁的线程是否当前线程
getExclusiveOwnerThread() != current)
//不是的话返回-1表示已经有其他线程加了写锁(后面就需要入AQS队列阻塞等待唤醒)
return -1;
//走到这里说明没有线程加写锁或者加写锁的就是本线程
//从state中获取共享锁的数目(读锁的数目)
int r = sharedCount(c);
//非公平锁:这里调用NonfairSync的readerShouldBlock判断是否需要阻塞读
//返回false的话不需要阻塞,接着执行&&后面的逻辑
if (!readerShouldBlock() &&
//判断读锁数量是否小于最大数目
r < MAX_COUNT &&
//使用CAS加读锁
compareAndSetState(c, c + SHARED_UNIT)) {
//上面的if中的逻辑执行完毕后,锁state已经加好了读锁
//下面的逻辑负责处理其他需要修改的属性
//第一次加读锁
if (r == 0) {
//设置第一次加锁的线程以及初始化读锁数量
firstReader = current;
firstReaderHoldCount = 1;
//第n次加锁的仍旧是第一次加锁的线程
} else if (firstReader == current) {
//修改读锁数量即可
firstReaderHoldCount++;
//第n次加锁的不是第一次加锁的线程
} else {
//cachedHoldCounter是缓存
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
//返回1(正数)表示加读锁成功
return 1;
}
//走到这里说明发生了以下几种情况:
//1.需要阻塞读
//2.读锁数目已经超过最大值
//3.CAS加读锁失败
return fullTryAcquireShared(current);
}
2.3.3.1.1.1. 判断是否需要阻塞读【非公平锁】
- ReentrantReadWriteLock.NonfairSync#readerShouldBlock
final boolean readerShouldBlock() {
//队头是互斥节点(加写锁的节点)情况下才需要阻塞读
//这就是非公平锁的特点
//即使队列前面已经有其他加读锁的线程等待我也不管
return apparentlyFirstQueuedIsExclusive();
}
- apparentlyFirstQueuedIsExclusive
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
//队列不为空
return (h = head) != null &&
//并且 队列的实际头节点【之所以说实际队列的头节点是个占位符】不为空
(s = h.next) != null &&
//并且 队列的实际头节点不是共享的头节点(即加的不是读锁)
!s.isShared() &&
//并且 队列的实际头节点的线程不为空
s.thread != null;
//满足以上所有情况才需要阻塞当前加读锁的线程
}
2.3.3.1.1.2. 快速尝试加锁失败,那么改用死循环加锁
- ReentrantReadWriteLock.Sync#fullTryAcquireShared
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
//死循环
for (;;) {
int c = getState();
//写锁的数量不为0
if (exclusiveCount(c) != 0) {
//且加写锁的不是本线程
if (getExclusiveOwnerThread() != current)
//返回-1
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)) {
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");
//加读锁,同上面的tryAcquireShared
if (compareAndSetState(c, c + SHARED_UNIT)) {
if (sharedCount(c) == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} 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.3.3.1.2. 尝试加锁失败,那么入AQS队列并阻塞,等待唤醒继续抢占锁
private void doAcquireShared(int arg) {
//构造SHARE节点加入AQS队列阻塞等待唤醒
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
//死循环抢占锁
for (;;) {
//当前节点的前一个节点是头节点
final Node p = node.predecessor();
if (p == head) {
//继续尝试加共享锁
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
//判断是否需要阻塞
if (shouldParkAfterFailedAcquire(p, node) &&
//需要的话进行阻塞
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
2.3.3.1.2.1. 加入AQS队列并阻塞
- addWaiter
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
//快速尝试加入队尾
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//快速尝试加入队尾失败,那么改用enq加入队尾
enq(node);
return node;
}
参考:5.AQS.md
2.3.4. 读锁解锁
- ReentrantReadWriteLock.ReadLock#unlock
public void unlock() {
//使用AQS解共享锁
sync.releaseShared(1);
}
2.3.4.1. 使用AQS解共享锁
- AbstractQueuedSynchronizer#releaseShared
public final boolean releaseShared(int arg) {
//调用Sync尝试解共享锁
//如果共享锁【读锁】已经全部释放完,那么执行doReleaseShared
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
2.3.4.1.1. 使用Sync尝试解锁
- ReentrantReadWriteLock.Sync#tryReleaseShared
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;
}
//死循环修改state数量
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
//CAS修改state数量
if (compareAndSetState(c, nextc))
return nextc == 0;//减为0了那么返回true
}
}
2.3.4.1.2. 所有共享锁都被释放,唤醒AQS队列中头节点的下一个节点
- AbstractQueuedSynchronizer#doReleaseShared
private void doReleaseShared() {
for (;;) {
Node h = head;
//队列不为空
if (h != null && h != tail) {
int ws = h.waitStatus;
//头节点的状态为SIGNAL【即承诺唤醒下一个节点】
if (ws == Node.SIGNAL) {
//CAS设置状态为0
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
//唤醒头节点的下一个节点
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
2.3.4.1.2.1. 唤醒AQS队列中头节点的下一个节点
- unparkSuccessor
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
//如果当前节点的状态是正常的?
if (ws < 0)
//当前节点的状态<0,则把状态改为0
//0是空的状态,因为node这个节点的线程释放了锁后续不需要做任何
compareAndSetWaitStatus(node, ws, 0);
//获取当前节点的下一个节点
Node s = node.next;
//如果下一个节点是空(即当前节点是尾节点)或者下一个节点的状态>0(取消)
if (s == null || s.waitStatus > 0) {
s = null;
//从尾节点往前遍历至当前节点
for (Node t = tail; t != null && t != node; t = t.prev)
//找到最靠近当前节点的状态<=0(非取消)的节点
if (t.waitStatus <= 0)
s = t;
}
//唤醒当前节点的下一个节点
if (s != null)
LockSupport.unpark(s.thread);
}
2.3.5. 写锁加锁
- writeLock
//返回的就是写锁
public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
- ReentrantReadWriteLock.WriteLock#lock
public void lock() {
//调用AQS加锁
sync.acquire(1);
}
2.3.5.1. 调用AQS加互斥锁
- AQS.acquire
public final void acquire(int arg) {
//tryAcquire加锁成功则返回true,那么执行业务逻辑
//加锁失败返回false,那么需要加入阻塞队列,阻塞等待唤醒
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
2.3.5.1.1. 使用Sync尝试加互斥锁
- ReentrantReadWriteLock.Sync#tryAcquire
protected final boolean tryAcquire(int acquires) {
//获取当前线程
Thread current = Thread.currentThread();
//获取state数量【读写锁的数量】
int c = getState();
//从state中写锁的数量
int w = exclusiveCount(c);
//已经加了写锁
if (c != 0) {
//但是加锁的不是我,那么返回false表示加锁失败
// (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数量
setState(c + acquires);
return true;
}
//走到这里说明还没有人加写锁
//这里写需要阻塞
if (writerShouldBlock() ||
//或者 CAS加锁失败
!compareAndSetState(c, c + acquires))
//那么返回false
return false;
//走到这里说明上面的逻辑加写锁成功,设置占有锁的线程为当前线程
setExclusiveOwnerThread(current);
return true;
}
2.3.5.1.1.1. 判断写是否需要阻塞【非公平锁】
- ReentrantReadWriteLock.NonfairSync#writerShouldBlock
final boolean writerShouldBlock() {
//写默认不需要阻塞
return false; // writers can always barge
}
2.3.5.1.2. 尝试加锁失败,那么入AQS队列并阻塞,等待唤醒继续抢占锁
- addWaiter
//...
- acquireQueued
//...
参考:5.AQS.md
2.3.6. 写锁解锁
- ReentrantReadWriteLock.WriteLock#unlock
public void unlock() {
//AQS解锁
sync.release(1);
}
2.3.6.1. 调用AQS解互斥锁
- AbstractQueuedSynchronizer#release
public final boolean release(int arg) {
//调用Sync尝试解互斥锁
//如果互斥量全部解锁完了,那么返回true,执行下面的逻辑
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//唤醒AQS队列中的头节点
unparkSuccessor(h);
return true;
}
return false;
}
2.3.6.1.1. 调用Sync尝试解互斥锁
- ReentrantReadWriteLock.Sync#tryRelease
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//减state量
int nextc = getState() - releases;
//写锁的互斥量是否减为0
boolean free = exclusiveCount(nextc) == 0;
if (free)
//减为0了那么修改占用锁的线程为空
setExclusiveOwnerThread(null);
//修改state量
setState(nextc);
return free;
}
2.3.6.1.2. 尝试解锁成功后,唤醒AQS队列中头节点的下一个节点
- unparkSuccessor
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
//如果当前节点的状态是正常的?
if (ws < 0)
//当前节点的状态<0,则把状态改为0
//0是空的状态,因为node这个节点的线程释放了锁后续不需要做任何
compareAndSetWaitStatus(node, ws, 0);
//获取当前节点的下一个节点
Node s = node.next;
//如果下一个节点是空(即当前节点是尾节点)或者下一个节点的状态>0(取消)
if (s == null || s.waitStatus > 0) {
s = null;
//从尾节点往前遍历至当前节点
for (Node t = tail; t != null && t != node; t = t.prev)
//找到最靠近当前节点的状态<=0(非取消)的节点
if (t.waitStatus <= 0)
s = t;
}
//唤醒当前节点的下一个节点
if (s != null)
LockSupport.unpark(s.thread);
}
3. 公平ReentrantReadWriteLock
3.1. 是什么
无论队列前面是否有人排队等待锁,我直接去抢
3.2. 怎么使用
public class ReadWriteLockTest
{
private static ReadWriteLock lock = new ReentrantReadWriteLock(true);//true表示公平
private static Lock readLock = lock.readLock();
private static Lock writeLock = lock.writeLock();
private static List<Integer> data = new ArrayList<>();
public static void main(String[] args) throws InterruptedException
{
Thread readThread = new Thread(() -> {
while (true)
{
try
{
TimeUnit.MILLISECONDS.sleep(500);
readLock.lock();
System.out.println(Thread.currentThread().getName() + " read: " + data);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
readLock.unlock();
}
}
});
Thread readThread2 = new Thread(() -> {
while (true)
{
try
{
TimeUnit.MILLISECONDS.sleep(300);
readLock.lock();
System.out.println(Thread.currentThread().getName() + " read: " + data);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
readLock.unlock();
}
}
});
Thread writeThread = new Thread(() -> {
int i = 0;
while (true)
{
try
{
TimeUnit.MILLISECONDS.sleep(200);
writeLock.lock();
if (i % 2 == 0)
{
data.add(i);
}else
{
data.remove(0);
}
i++;
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
writeLock.unlock();
}
}
});
readThread.start();
readThread2.start();
writeThread.start();
readThread.join();
readThread2.join();
writeThread.join();
}
}
3.3. 源码分析
3.3.1. uml
@startuml
skinparam classAttributeIconSize 0
interface AQS{
}
class Sync{
}
interface Lock{
}
interface ReadWriteLock{
}
class ReentrantReadWriteLock{
}
class WriteLock{
}
class ReadLock{
}
class WriteLock{
}
Lock <|-- ReadLock
Lock <|-- WriteLock
ReadWriteLock <|-- ReentrantReadWriteLock
AQS <|-- Sync
ReentrantReadWriteLock --> ReadLock
ReentrantReadWriteLock --> WriteLock
ReadLock --> Sync
WriteLock --> Sync
@enduml
3.3.2. 构造方法
- ReentrantReadWriteLock
public ReentrantReadWriteLock(boolean fair) {
//初始化了Sync
//true的话使用的是FairSync
sync = fair ? new FairSync() : new NonfairSync();
//初始化读写锁
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
- ReentrantReadWriteLock.ReadLock
protected ReadLock(ReentrantReadWriteLock lock) {
//其实就是保存了ReentrantReadWriteLock的Sync
sync = lock.sync;
}
- ReentrantReadWriteLock.WriteLock
protected WriteLock(ReentrantReadWriteLock lock) {
//其实就是保存了ReentrantReadWriteLock的Sync
sync = lock.sync;
}
3.3.3. 读锁加锁
- ReentrantReadWriteLock.readLock
//返回就是读锁
public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }
- ReadLock.lock
public void lock() {
//使用AQS加共享锁
sync.acquireShared(1);
}
3.3.3.1. 使用AQS加共享锁
- AQS acquireShared
public final void acquireShared(int arg) {
//ReentrantReadWriteLock的Sync重写了tryAcquireShared
//所以这里调用的是Sync的tryAcquireShared
//如果返回<0说明尝试加锁失败,执行doAcquireShared入AQS队列并阻塞,等待唤醒
//返回>=0说明加锁成功,执行后续的业务逻辑
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
3.3.3.1.1. 使用Sync尝试加共享锁
- ReentrantReadWriteLock.Sync.tryAcquireShared
protected final int tryAcquireShared(int unused) {
//当前线程
Thread current = Thread.currentThread();
//当前的state数目(或者说加锁【既包括读锁也包括写锁】的数量)
int c = getState();
//从state中获取互斥锁的数目(写锁的数量)
//如果数量不为0(即>0)说明已经加了写锁
if (exclusiveCount(c) != 0 &&
//判断加锁的线程是否当前线程
getExclusiveOwnerThread() != current)
//不是的话返回-1表示已经有其他线程加了写锁(后面就需要入AQS队列阻塞等待唤醒)
return -1;
//走到这里说明没有线程加写锁或者加写锁的就是本线程
//从state中获取共享锁的数目(读锁的数目)
int r = sharedCount(c);
//非公平锁:这里调用NonfairSync的readerShouldBlock判断是否需要阻塞读
//返回false的话不需要阻塞,接着执行&&后面的逻辑
if (!readerShouldBlock() &&
//判断读锁数量是否小于最大数目
r < MAX_COUNT &&
//使用CAS加读锁
compareAndSetState(c, c + SHARED_UNIT)) {
//上面的if中的逻辑执行完毕后,锁state已经加好了读锁
//下面的逻辑负责处理其他需要修改的属性
//第一次加读锁
if (r == 0) {
//设置第一次加锁的线程以及初始化读锁数量
firstReader = current;
firstReaderHoldCount = 1;
//第n次加锁的仍旧是第一次加锁的线程
} else if (firstReader == current) {
//修改读锁数量即可
firstReaderHoldCount++;
//第n次加锁的不是第一次加锁的线程
} else {
//cachedHoldCounter是缓存
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
//返回1(正数)表示加读锁成功
return 1;
}
//走到这里说明发生了以下几种情况:
//1.需要阻塞读
//2.读锁数目已经超过最大值
//3.CAS加读锁失败
return fullTryAcquireShared(current);
}
3.3.3.1.1.1. 判断是否需要阻塞读【公平】
- ReentrantReadWriteLock.FairSync#readerShouldBlock
final boolean readerShouldBlock() {
//队列头节点为空或者这个头节点的线程不是当前线程的时候读需要阻塞
return hasQueuedPredecessors();
}
- AQS hasQueuedPredecessors
public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
//队列实际头节点为空或者这个头节点的线程不是当前线程
//那么返回true
((s = h.next) == null || s.thread != Thread.currentThread());
}
3.3.3.1.1.2. 快速尝试加锁失败,那么改用死循环加锁
- ReentrantReadWriteLock.Sync#fullTryAcquireShared
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
//死循环
for (;;) {
int c = getState();
//写锁的数量不为0
if (exclusiveCount(c) != 0) {
//且加写锁的不是本线程
if (getExclusiveOwnerThread() != current)
//返回-1
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)) {
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");
//加读锁,同上面的tryAcquireShared
if (compareAndSetState(c, c + SHARED_UNIT)) {
if (sharedCount(c) == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} 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;
}
}
}
3.3.3.1.2. 尝试加锁失败,那么入AQS队列并阻塞,等待唤醒继续抢占锁
private void doAcquireShared(int arg) {
//构造SHARE节点加入AQS队列阻塞等待唤醒
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
//死循环抢占锁
for (;;) {
//当前节点的前一个节点是头节点
final Node p = node.predecessor();
if (p == head) {
//继续尝试加共享锁
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
//判断是否需要阻塞
if (shouldParkAfterFailedAcquire(p, node) &&
//需要的话进行阻塞
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
3.3.3.1.2.1. 加入AQS队列并阻塞
- addWaiter
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
//快速尝试加入队尾
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//快速尝试加入队尾失败,那么改用enq加入队尾
enq(node);
return node;
}
参考:5.AQS.md
3.3.4. 读锁解锁
- ReentrantReadWriteLock.ReadLock#unlock
public void unlock() {
//使用AQS解共享锁
sync.releaseShared(1);
}
3.3.4.1. 使用AQS解共享锁
- AbstractQueuedSynchronizer#releaseShared
public final boolean releaseShared(int arg) {
//调用Sync尝试解共享锁
//如果共享锁【读锁】已经全部释放完,那么执行doReleaseShared
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
3.3.4.1.1. 使用Sync尝试解锁
- ReentrantReadWriteLock.Sync#tryReleaseShared
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;
}
//死循环修改state数量
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
//CAS修改state数量
if (compareAndSetState(c, nextc))
return nextc == 0;//减为0了那么返回true
}
}
3.3.4.1.2. 所有共享锁都被释放,唤醒AQS队列中头节点的下一个节点
- AbstractQueuedSynchronizer#doReleaseShared
private void doReleaseShared() {
for (;;) {
Node h = head;
//队列不为空
if (h != null && h != tail) {
int ws = h.waitStatus;
//头节点的状态为SIGNAL【即承诺唤醒下一个节点】
if (ws == Node.SIGNAL) {
//CAS设置状态为0
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
//唤醒头节点的下一个节点
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
3.3.4.1.2.1. 唤醒AQS队列中头节点的下一个节点
- unparkSuccessor
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
//如果当前节点的状态是正常的?
if (ws < 0)
//当前节点的状态<0,则把状态改为0
//0是空的状态,因为node这个节点的线程释放了锁后续不需要做任何
compareAndSetWaitStatus(node, ws, 0);
//获取当前节点的下一个节点
Node s = node.next;
//如果下一个节点是空(即当前节点是尾节点)或者下一个节点的状态>0(取消)
if (s == null || s.waitStatus > 0) {
s = null;
//从尾节点往前遍历至当前节点
for (Node t = tail; t != null && t != node; t = t.prev)
//找到最靠近当前节点的状态<=0(非取消)的节点
if (t.waitStatus <= 0)
s = t;
}
//唤醒当前节点的下一个节点
if (s != null)
LockSupport.unpark(s.thread);
}
3.3.5. 写锁加锁
- writeLock
//返回的就是写锁
public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
- ReentrantReadWriteLock.WriteLock#lock
public void lock() {
//调用AQS加锁
sync.acquire(1);
}
3.3.5.1. 调用AQS加互斥锁
- AQS.acquire
public final void acquire(int arg) {
//tryAcquire加锁成功则返回true,那么执行业务逻辑
//加锁失败返回false,那么需要加入阻塞队列,阻塞等待唤醒
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
3.3.5.1.1. 使用Sync尝试加互斥锁
- ReentrantReadWriteLock.Sync#tryAcquire
protected final boolean tryAcquire(int acquires) {
//获取当前线程
Thread current = Thread.currentThread();
//获取state数量【读写锁的数量】
int c = getState();
//从state中写锁的数量
int w = exclusiveCount(c);
//已经加了写锁
if (c != 0) {
//但是加锁的不是我,那么返回false表示加锁失败
// (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数量
setState(c + acquires);
return true;
}
//走到这里说明还没有人加写锁
//这里写需要阻塞
if (writerShouldBlock() ||
//或者 CAS加锁失败
!compareAndSetState(c, c + acquires))
//那么返回false
return false;
//走到这里说明上面的逻辑加写锁成功,设置占有锁的线程为当前线程
setExclusiveOwnerThread(current);
return true;
}
3.3.5.1.1.1. 判断写是否需要阻塞【公平】
- ReentrantReadWriteLock.FairSync#writerShouldBlock
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
- AQS hasQueuedPredecessors
public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
//队列实际头节点为空或者这个头节点的线程不是当前线程
//那么返回true
((s = h.next) == null || s.thread != Thread.currentThread());
}
3.3.5.1.2. 尝试加锁失败,那么入AQS队列并阻塞,等待唤醒继续抢占锁
- addWaiter
//...
- acquireQueued
//...
参考:5.AQS.md
3.3.6. 写锁解锁
- ReentrantReadWriteLock.WriteLock#unlock
public void unlock() {
//AQS解锁
sync.release(1);
}
3.3.6.1. 调用AQS解互斥锁
- AbstractQueuedSynchronizer#release
public final boolean release(int arg) {
//调用Sync尝试解互斥锁
//如果互斥量全部解锁完了,那么返回true,执行下面的逻辑
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//唤醒AQS队列中的头节点
unparkSuccessor(h);
return true;
}
return false;
}
3.3.6.1.1. 调用Sync尝试解互斥锁
- ReentrantReadWriteLock.Sync#tryRelease
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//减state量
int nextc = getState() - releases;
//写锁的互斥量是否减为0
boolean free = exclusiveCount(nextc) == 0;
if (free)
//减为0了那么修改占用锁的线程为空
setExclusiveOwnerThread(null);
//修改state量
setState(nextc);
return free;
}
3.3.6.1.2. 尝试解锁成功后,唤醒AQS队列中头节点的下一个节点
- unparkSuccessor
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
//如果当前节点的状态是正常的?
if (ws < 0)
//当前节点的状态<0,则把状态改为0
//0是空的状态,因为node这个节点的线程释放了锁后续不需要做任何
compareAndSetWaitStatus(node, ws, 0);
//获取当前节点的下一个节点
Node s = node.next;
//如果下一个节点是空(即当前节点是尾节点)或者下一个节点的状态>0(取消)
if (s == null || s.waitStatus > 0) {
s = null;
//从尾节点往前遍历至当前节点
for (Node t = tail; t != null && t != node; t = t.prev)
//找到最靠近当前节点的状态<=0(非取消)的节点
if (t.waitStatus <= 0)
s = t;
}
//唤醒当前节点的下一个节点
if (s != null)
LockSupport.unpark(s.thread);
}
4. 参考
Java源码分析系列笔记-17.ReentrantReadWriteLock的更多相关文章
- Java源码分析系列之HttpServletRequest源码分析
从源码当中 我们可以 得知,HttpServletRequest其实 实际上 并 不是一个类,它只是一个标准,一个 接口而已,它的 父类是ServletRequest. 认证方式 public int ...
- Java源码分析系列
1) 深入Java集合学习系列:HashMap的实现原理 2) 深入Java集合学习系列:LinkedHashMap的实现原理 3) 深入Java集合学习系列:HashSet的实现原理 4) 深入Ja ...
- MyBatis 源码分析系列文章导读
1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章.本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(how)等三个角度进行了说 ...
- spring源码分析系列 (8) FactoryBean工厂类机制
更多文章点击--spring源码分析系列 1.FactoryBean设计目的以及使用 2.FactoryBean工厂类机制运行机制分析 1.FactoryBean设计目的以及使用 FactoryBea ...
- Spring IOC 容器源码分析系列文章导读
1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...
- jQuery源码分析系列
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...
- MyCat源码分析系列之——结果合并
更多MyCat源码分析,请戳MyCat源码分析系列 结果合并 在SQL下发流程和前后端验证流程中介绍过,通过用户验证的后端连接绑定的NIOHandler是MySQLConnectionHandler实 ...
- MyCat源码分析系列之——BufferPool与缓存机制
更多MyCat源码分析,请戳MyCat源码分析系列 BufferPool MyCat的缓冲区采用的是java.nio.ByteBuffer,由BufferPool类统一管理,相关的设置在SystemC ...
- jquery2源码分析系列
学习jquery的源码对于提高前端的能力很有帮助,下面的系列是我在网上看到的对jquery2的源码的分析.等有时间了好好研究下.我们知道jquery2开始就不支持IE6-8了,从jquery2的源码中 ...
- [Tomcat 源码分析系列] (二) : Tomcat 启动脚本-catalina.bat
概述 Tomcat 的三个最重要的启动脚本: startup.bat catalina.bat setclasspath.bat 上一篇咱们分析了 startup.bat 脚本 这一篇咱们来分析 ca ...
随机推荐
- HarmonyOS NEXT 基于原生能力获取视频缩略图
大家好,我是 V 哥. 不得不佩服 HarmonyOS NEXT 原生能力的强大,如果你想在 鸿蒙 APP 开发中获取视频缩略图,不用依赖第三方库,就可以高效和稳定的实现,AVMetadataHelp ...
- AI+Web3:张三的未来机会地图
热点风暴:ETHDenver 2025掀起热潮 滑动微信的那一刻,张三愣住了.热搜上,ETHDenver大会简直是技术圈的春晚.AI和Web3,这两个曾经高高在上的概念,突然变得触手可及.大会透露的一 ...
- [每日算法] leetcode第3题:无重复字符的最长子串
leetcode第3题入口 题目描述 给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: s = "abcabcbb" 输出: 3 解法1: ...
- 流式计算(四)-Flink Stream API 篇二
个人原创文章,禁止任何形式转载,否则追究法律责任! 本文只发表在"公众号"和"博客园",其他均属复制粘贴!如果觉得排版不清晰,请查看公众号文章. 话说看图看核心 ...
- wrk
github.com/wg/wrk 是一个现代的 HTTP 基准测试工具.
- CSS布局——左右固定中间填满
小小例子,注意中间的div应该写在最后,留爪. 先上个高清无码图 源码实现 <!DOCTYPE html> <html lang="en" xmlns=" ...
- Springboot+MongoDB添加数据时会自带_class字段
_class字段作用 帮助映射子类,为了方便处理Pojo中存在继承的情况,增加系统的扩展性 去除_class字段 新增mongodb的配置类,配置mappingMongoConverter,配置类网上 ...
- Selenium+pytest 页面对象模型框架
下载地址:https://gitee.com/xiaopo1998/web_ui_test.git Selenium 页面对象模型框架使用说明 本框架基于 Selenium WebDriver 实现了 ...
- FastAPI与SQLAlchemy同步数据库集成
title: FastAPI与SQLAlchemy同步数据库集成 date: 2025/04/15 01:27:37 updated: 2025/04/15 01:27:37 author: cmdr ...
- MySQL 三层 B+ 树能存多少数据?
MySQL 三层 B+ 树能存多少数据? MySQL 的 B+ 树索引结构广泛用于存储引擎(如 InnoDB)中,用于实现高效的查询.B+ 树的高度和每一层的节点数量直接影响其存储能力.以下是计算三层 ...