【问题标题】:Why AbstractOwnableSynchronizer.exclusiveOwnerThread is not declared as volatile?为什么 AbstractOwnableSynchronizer.exclusiveOwnerThread 未声明为 volatile?
【发布时间】:2020-03-23 07:01:04
【问题描述】:

在阅读java.util.concurrent.locks.ReentrantLock的源码时发现tryLock()方法的实现如下:

        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

根据AbstractQueuedSynchronizer 中维护的state,我们尝试“拥有”锁或检查我们是否已经拥有锁。但我想知道为什么变量 state 被声明为 volatile 而变量 exclusiveOwnerThread 不是?

【问题讨论】:

    标签: java volatile reentrantlock


    【解决方案1】:

    要了解为什么exclusiveOwnerThread 不需要是易失性的,将获取和释放方法一起查看会有所帮助。

    获取方法1

    /**
     * Performs non-fair tryLock.  tryAcquire is implemented in
     * subclasses, but both need nonfair try for trylock method.
     */
    @ReservedStackAccess
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
    

    发布方式:

    @ReservedStackAccess
    protected final boolean tryRelease(int releases) {
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
    

    同样重要的是要意识到exclusiveOwnerThread 不会引用与所涉及的线程无关的任意对象。它专门持有对Thread 实例的引用,并严格与调用线程进行比较。换句话说,重要的是:

    Thread.currentThread() == getExclusiveOwnerThread()
    

    由于#nonfairTryAcquire(int)#tryRelease(int) 的组合性质,当且仅当调用线程先前调用了#setExclusiveOwnerThread(Thread),并且将其自身作为参数时,这将是正确的。一个线程中的操作总是发生在同一线程中的后续操作。

    所以如果c != 0那么有两种情况:

    1. 调用线程拥有同步器。

      • 由于一个线程中的操作总是发生在同一线程中的后续操作,因此保证getExclusiveOwnerThread() 将返回对调用线程的引用。
    2. 调用线程拥有同步器。

      • getExclusiveOwnerThread() 返回的引用不再重要,因为该方法不可能返回对调用线程的引用。

        由于#tryRelease(int) 中的setExclusiveOwnerThread(null) 调用,调用线程永远不会看到对自身的过时引用。这意味着getExclusiveOwnerThread() 可以返回null 或其他一些Thread 引用(无论是否过期),但绝不是对调用线程的引用。

    state 必须是 volatile 的原因是因为它在线程之间以一种最重要的方式共享,每个线程都能看到最新的值。


    1. FairSync#tryAcquire(int) 的实现几乎相同,只是它考虑了调用线程的顺序。

    【讨论】:

    • 非常感谢您的详细回答 :) 我同意当线程将自己与 exclusiveOwnerThread 值进行比较时它运行良好,因为其中的 happens-before 规则一个线程。如果线程已经拥有了锁,那么在重新进入或解锁时,它肯定会得到正确的exclusiveOwnerThread(指的是它自己)。正如您所说,如果线程不拥有锁(由setExclusiveOwnerThread(null) 完成),则足以保证线程不会看到引用自身的陈旧值。
    • 但让我感到困惑的是ReentrantLock 中定义了一个getOwner() 方法,它在c !=0 的情况下返回exclusiveOwnerThread。在nonfairTryAcquire()c == 0 分支中,exclusiveOwnerThread 变量在 volatile-write(其作用类似于解锁,以实现内存一致性)后设置为状态。所以我认为getOwner() 方法可能会返回一个陈旧的值exclusiveOwnerThread,即使它总是在exclusiveOwnerThread 读取之前的状态上触发易失性读取(其作用类似于锁定,以实现内存一致性)...
    • ReentrantLock#getOwner() 的文档说,“当这个方法被一个不是所有者的线程调用时,返回值反映了当前锁状态的最大努力近似值”。我将其解释为,如果调用线程不拥有锁,则无法保证您会看到正确的值。
    • 对不起,我忘了阅读该方法的文档...非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-07
    • 1970-01-01
    • 1970-01-01
    • 2019-01-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多