06.JUC 锁 - AQS - 共享模式

基本概念

AQS 的共享模式,表示 AQS 通过共享模式获取/释放锁。该类对应的方法为 acquireShared/acquireSharedInterruptibly/tryAcquireSharedNanos、 releaseShared

acquireShared

acquireShared 表示以共享模式获取锁对象,并忽略中断。

public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0) {
        doAcquireShared(arg);
    }
}
  • tryAcquireShared: 与独占模式一样留给子类实现。
  • doAcquireShared:负责获取锁失败后的操作。

1.doAcquireShared

下面来看 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) {
                // 再次尝试获取[读锁]
                int r = tryAcquireShared(arg);

                // 成功则更新等待队列的头节点
                if (r >= 0) {
                    // 关键
                    setHeadAndPropagate(node, r);
                    p.next = null; 
                    if (interrupted){
                        selfInterrupt();
                    }
                    failed = false;
                    return;
                }
            }

            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt()){
                interrupted = true;
            }   
        }
    } finally {
        if (failed){
            cancelAcquire(node);
        }   
    }
}

2.setHeadAndPropagate

该方法负责设置新的头节点,并执行传播操作。

传播操作具体是节点指唤等待队列的第一个节点后,被唤醒的节点由于自旋的缘故又会调用该方法唤醒其后续点,直到整个队列都被唤醒。

下面来看它的实现过程:

private void setHeadAndPropagate(Node node, int propagate) {
    // 设置新的头节点,即出队操作
    Node h = head; 
    setHead(node);

    // 唤醒该节点的下个节点
    if (propagate > 0 || h == null || h.waitStatus < 0) {
        Node s = node.next;
        if (s == null || s.isShared()){
            // 释放共享锁操作,实质就是唤醒等待队列的节点
            doReleaseShared();
        }   
    }
}

3.doReleaseShared

再来看看 doReleaseShared 的实现过程:

private void doReleaseShared() {
    // 循环,直至唤醒同步等待队列中的所有节点
    for (;;) {
        Node h = head;

        // 判断同步等待队列是否为空
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)){
                    continue; // loop to recheck cases
                }

                // 唤醒 h 的后继节点(同时将 h 等待状态置为 CANCELED)
                unparkSuccessor(h);

            } else if (ws == 0 && 
                !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)){
                continue; // loop on failed CAS
            }
        }


        if (h == head){
            break;// loop if head changed
        }   
    }
}

整个过程如下图所示:

《06.JUC 锁 - AQS - 共享模式》

acquireSharedInterruptibly

acquireSharedInterruptibly表示以共享模式获取锁对象,不忽略中断。

public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
    if (Thread.interrupted()){
        // 抛出异常...
    }

    if (tryAcquireShared(arg) < 0){
        doAcquireSharedInterruptibly(arg);
    }
}

关键来看 doAcquireSharedInterruptibly,它与 doAcquireShared 的区别在于,如果发现线程的中断标志位被置为 true,则会抛出异常中断线程。区别如下:

if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt()){
    throw new InterruptedException();
}          

tryAcquireSharedNanos

tryAcquireSharedNanos表示以共享模式获取锁对象,尝试获取失败,则线程进入阻塞状态一段时间。线程在进入阻塞状态后有两种情况:

  • 进入阻塞状态,一直到 nanosTimeout 超时,返回 false;
  • 进入阻塞状态,时长未到 nanosTimeout 被唤醒,则通过自旋再次尝试,成功获取锁或直到超时返回结果。

下面来看它的实现过程:

public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException {
    if (Thread.interrupted()){
        // 抛出异常...
    }
    return tryAcquireShared(arg) >= 0 || 
        // 实现原理与独占模式的 doAcquireNanos 类似,这里不再分析
        doAcquireSharedNanos(arg, nanosTimeout);
}

releaseShared

该操作表示通过共享模式释放锁对象。

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        // 关键
        doReleaseShared();
        return true;
    }
    return false;
}
    原文作者:JUC
    原文地址: https://blog.csdn.net/u012420654/article/details/56488775
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞