共享锁除了可以多个线程共享外,在共享节点间还具有传播性。何为传播性,先看共享锁获取锁的代码:
//java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireShared
public final void acquireShared(int arg) {
//tryAcquireShared 返回-1获取锁失败,返回值大于1或者0获取锁成功
if (tryAcquireShared(arg) < 0)
//获取锁失败,进入队列操作
doAcquireShared(arg);
}
tryAcquireShared在ReentrantReadWriteLock中的实现,返回值只有两种,1表示获取锁成功,-1表示获取锁失败。在Semaphore中tryAcquireShared的返回值代表资源剩余量,返回值大于等于0表示获取锁成功,小于0表示获取锁失败。
tryAcquireShared获取锁失败后,进入AQS同步队列操作doAcquireShared。创建共享节点node,并CAS排到队列尾部,接下来判断是应该阻塞还是继续获取锁。当node的前驱节点是head时,尝试获取锁tryAcquireShared,如果获取锁成功返回值r >= 0,则执行函数setHeadAndPropagate,这个函数就是共享锁的传播性。
//java.util.concurrent.locks.AbstractQueuedSynchronizer#doAcquireShared
private void doAcquireShared(int arg) {
//创建一个读节点,并入队列
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;😉 {
final Node p = node.predecessor();
if (p == head) {
//如果前继节点是head,则尝试获取锁
int r = tryAcquireShared(arg);
if (r >= 0) {
//获取锁成功,设置新head和共享传播(唤醒后继共享节点)
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
/**
-
p不是头结点 or 获取锁失败,判断是否应该被阻塞
-
前继节点的ws = SIGNAL 时应该被阻塞
*/
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
node获取锁成功出队,设置新head,并将共享性传播给后继节点,即唤醒后继共享节点。为什么当一个节点的线程获取共享锁后,要唤醒后继共享节点?共享锁是可以多个线程共有的,当一个节点的线程获取共享锁后,必然要通知后继共享节点的线程,也可以获取锁了,这样就不会让其他等待的线程等很久,而传播性的目的也是尽快通知其他等待的线程尽快获取锁。
private void setH

本文详细解释了Java并发编程中`acquireShared`方法的工作原理,特别是共享锁的传播性,涉及ReentrantReadWriteLock和Semaphore的实现,以及doAcquireShared方法如何在同步队列中操作。文章通过例子展示了共享锁传播性在多线程环境中的作用和必要性。
最低0.47元/天 解锁文章
——从PROPAGATE和setHeadAndPropagate分析共享锁的传播性&spm=1001.2101.3001.5002&articleId=137739882&d=1&t=3&u=81f12802227a4608afef64ace9efdc95)
6904

被折叠的 条评论
为什么被折叠?



