
通过上一篇《Java 并发(2)AbstractQueuedSynchronizer 源码分析之独占模式》的分析,我们知道了独占模式获取锁有三种方式,分别是不响应线程中断获取,响应线程中断获取,设置超时时间获取。在共享模式下获取锁的方式也是这三种,而且基本上都是大同小异,我们搞清楚了一种就能很快的理解其他的方式。
虽然说 AbstractQueuedSynchronizer 源码有一千多行,但是重复的也比较多,所以读者不要刚开始的时候被吓到,只要耐着性子去看慢慢的自然能够渐渐领悟。就我个人经验来说,阅读 AbstractQueuedSynchronizer 源码有几个比较关键的地方需要弄明白,分别是独占模式和共享模式的区别,结点的等待状态,以及对条件队列的理解。理解了这些要点那么后续源码的阅读将会轻松很多。
当然这些在《Java 并发(1)AbstractQueuedSynchronizer 源码分析之概要分析》这篇文章里都有详细的介绍,读者可以先去查阅。本篇对于共享模式的分析也是分为三种获取锁的方式和一种释放锁的方式。
1、不响应线程中断的获取
//以不可中断模式获取锁(共享模式)
public final void acquireShared(int arg) {
//1.尝试去获取锁
if (tryAcquireShared(arg) < 0) {
//2.如果获取失败就进入这个方法
doAcquireShared(arg);
}
}
//尝试去获取锁(共享模式)
//负数:表示获取失败
//零值:表示当前结点获取成功, 但是后继结点不能再获取了
//正数:表示当前结点获取成功, 并且后继结点同样可以获取成功
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
//在同步队列中获取(共享模式)
private void doAcquireShared(int arg) {
//添加到同步队列中
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//获取当前结点的前继结点
final Node p = node.predecessor();
//如果前继结点为head结点就再次尝试去获取锁
if (p == head) {
//再次尝试去获取锁并返回获取状态
//r < 0, 表示获取失败
//r = 0, 表示当前结点获取成功, 但是后继结点不能再获取了
//r > 0, 表示当前结点获取成功, 并且后继结点同样可以获取成功
int r = tryAcquireShared(arg);
if (r >= 0) {
//到这里说明当前结点已经获取锁成功了, 此时它会将锁的状态信息传播给后继结点
setHeadAndPropagate(node, r);
p.next = null;
//如果在线程阻塞期间收到中断请求, 就在这一步响应该请求
if (interrupted) {
selfInterrupt();
}
failed = false;
return;
}
}
//每次获取锁失败后都会判断是否可以将线程挂起, 如果可以的话就会在parkAndCheckInterrupt方法里将线程挂起
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) {
interrupted = true;
}
}
} finally {
if (failed) {
cancelAcquire(node);
}
}
}
//设置head结点并传播锁的状态(共享模式)
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head;
//将给定结点设置为head结点
setHead(node);
//如果propagate大于0表明锁可以获取了
if (propagate > 0 || h == null || h.waitStatus < 0) {
//获取给定结点的后继结点
Node s = node.next;
//如果给定结点的后继结点为空, 或者它的状态是共享状态
if (s == null || s.isShared()) {
//唤醒后继结点
doReleaseShared();
}
}
}
//释放锁的操作(共享模式)
private void doReleaseShared() {
for (;;) {
//获取同步队列的head结点
Node h = head;
if (h != null && h != tail) {
//获取head结点的等待状态
int ws = h.waitStatus;
//如果head结点的状态为SIGNAL, 表明后面有人在排队
if (ws == Node.SIGNAL) {
//先把head结点的等待状态更新为0
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) {
continue;
}
//再去唤醒后继结点
unparkSuccessor(h);
//如果head结点的状态为0, 表明此时后面没人在排队, 就只是将head状态修改为PROPAGATE
}else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) {
continue;
}
}
//只有保证期间head结点没被修改过才能跳出循环
if (h == head) {
break;
}
}
}
2、响应线程中断的获取
//以可中断模式获取锁(共享模式)
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
//首先判断线程是否中断, 如果是则抛出异常
if (Thread.interrupted()) {
throw new InterruptedException();
}
//1.尝试去获取锁
if (tryAcquireShared(arg) < 0) {
//2. 如果获取失败则进人该方法
doAcquireSharedInterruptibly(arg);
}
}
//以可中断模式获取(共享模式)
private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {
//将当前结点插入同步队列尾部
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
//获取当前结点的前继结点
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null;
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) {
//如果线程在阻塞过程中收到过中断请求, 那么就会立马在这里抛出异常
throw new InterruptedException();
}
}
} finally {
if (failed) {
cancelAcquire(node);
}
}
}
3、设置超时时间的获取
//以限定超时时间获取锁(共享模式)
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException {
if (Thread.interrupted()) {
throw new InterruptedException();
}
//1.调用tryAcquireShared尝试去获取锁
//2.如果获取失败就调用doAcquireSharedNanos
return tryAcquireShared(arg) >= 0 || doAcquireSharedNanos(arg, nanosTimeout);
}
//以限定超时时间获取锁(共享模式)
private boolean doAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException {
long lastTime = System.nanoTime();
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
//获取当前结点的前继结点
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null;
failed = false;
return true;
}
}
//如果超时时间用完了就结束获取, 并返回失败信息
if (nanosTimeout <= 0) {
return false;
}
//1.检查是否满足将线程挂起要求(保证前继结点状态为SIGNAL)
//2.检查超时时间是否大于自旋时间
if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold) {
//若满足上面两个条件就将当前线程挂起一段时间
LockSupport.parkNanos(this, nanosTimeout);
}
long now = System.nanoTime();
//超时时间每次减去获取锁的时间
nanosTimeout -= now - lastTime;
lastTime = now;
//如果在阻塞时收到中断请求就立马抛出异常
if (Thread.interrupted()) {
throw new InterruptedException();
}
}
} finally {
if (failed) {
cancelAcquire(node);
}
}
}
4、共享模式下结点的出队操作
//释放锁的操作(共享模式)
public final boolean releaseShared(int arg) {
//1.尝试去释放锁
if (tryReleaseShared(arg)) {
//2.如果释放成功就唤醒其他线程
doReleaseShared();
return true;
}
return false;
}
//尝试去释放锁(共享模式)
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
//释放锁的操作(共享模式)
private void doReleaseShared() {
for (;;) {
//获取同步队列的head结点
Node h = head;
if (h != null && h != tail) {
//获取head结点的等待状态
int ws = h.waitStatus;
//如果head结点的状态为SIGNAL, 表明后面有人在排队
if (ws == Node.SIGNAL) {
//先把head结点的等待状态更新为0
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) {
continue;
}
//再去唤醒后继结点
unparkSuccessor(h);
//如果head结点的状态为0, 表明此时后面没人在排队, 就只是将head状态修改为PROPAGATE
}else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) {
continue;
}
}
//只有保证期间head结点没被修改过才能跳出循环
if (h == head) {
break;
}
}
}

文章转载自JavaFish,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




