3. 从AbstractQueuedSynchronizer(AQS)说起2—共享模式的锁获取与释放

在上节中解析了AbstractQueuedSynchronizer(AQS)中独占模式对同步状态获取和释放的过程。本节将会对共享模式的同步状态获取和释放过程做一个解析。上一节提到了独占模式和共享模式的区别,最主要的区别就是在同一时刻能否有多个线程获取到同步状态。

1. 共享模式同步状态的获取

方法描述
public final void acquireShared(int arg)共享模式虾获取同步状态,忽略中断。

这个方法同独占模式获取同步状态的acquire方法一样,同样也是一个模板方法,我们简要回顾一下独占模式虾获取同步状态的acquire方法:

## AbstractQueuedSynchronizer#acquire
public final void acquire(int arg) {
    if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))    //获取锁(tryAcquire)->构造节点(addWaiter)->加入队列(addWaiter)->自旋获取锁(acquireQueued)
    selfInterrupt();    //中断当前线程
}
##AbstractQueuedSynchronizer#acquireShared
public final void acquireShared(int arg) {
    if (tryAcquireShared(arg)) //获取锁,由子类具体实现
    doAcquireShared(arg);
}
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 (inerrupted) 
                    selfInterrupt();
                failed = false;
                return;
            }
        }
        if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
        interrupted = true;
    }
} finally {
    if (failed)
        cancelAcquire(arg);    
    }
}

共享模式下获取同步状态的自旋过程和独占模式大致相同,从代码实现角度来看,不同的是共享模式下把线程构造节点接入队列,以及在获取同步状态后中断当前线程都放到了同一个方法里doAcquireShared。共享模式同样也是只有在是其前驱节点是头节点的时候,才会尝试获取同步状态,调用tryAcruireShared获取同步状态成功后,返回大于等于0的数,这个时候将会执行setHeadAndPropgate方法,这个方法首先会将当前获取同步状态的这个线程置位头节点(同独占模式一样),但在将当前线程置位头节点过后,又做了一部分操作,其代码如下:

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();    //唤醒后继节点,因为是共享模式,所以允许多个线程同时获取同步状态
    }
}

在doReleaseShared方法中,首先遍历队列中所有节点,如果节点状态为SIGNAL,则把SIGNAL状态置位0(初始状态),并调用unparkSuccessor把该节点的后继节点唤醒,如果该节点状态为0,则把状态置位PROPAGATE。

2. 共享模式同步状态的释放

方法描述
public final boolean releaseShared(int arg)释放同步状态,并唤醒后继节点

该方法的实现同独占模式类似,也是一个模板方法,具体的释放实现由子类自定义,在成功释放同步状态后,将会唤醒后继节点:

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {    //释放同步状态
        doReleaseShared();    //唤醒后继节点
        return true;
    }
    return false;
}

以上就是AQS中的共享模式对同步状态的获取与释放,在有了独占模式的分析过后,对共享模式的分析就显得要轻松的多。下一节将会继续探讨AbstractQueuedSynchronizer类中具体的一些细节问题。

转载于:https://my.oschina.net/waterfu/blog/2050953

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值