如何在ReentrantLock.Sync中搭载当前线程变量?

Dar*_*zAc 9 java concurrency multithreading synchronization memory-barriers

我在"实践中的Java并发"第14.6.1节中阅读了有关ReentrantLock实现的一些细节,注释中的某些内容让我感到困惑:

因为受保护的状态操作方法具有易失性读取或写入的内存语义,并且ReentrantLock 只有在调用getState之后才会读取所有者字段并且仅在调用setState之前写入它,ReentrantLock可以依赖于同步状态的内存语义,因此避免进一步同步见第16.1.4节.

它引用的代码:

protected boolean tryAcquire(int ignored) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c ==0) {
        if (compareAndSetState(0, 1)) {
             owner = current;
             return true;
        }
     } else if (current == owner) {
         setState(c+1);
         return true;
     }
     return false;
}
Run Code Online (Sandbox Code Playgroud)

我相信这是对简化代码nonfairTryAcquireReentrantLock.Sync.

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;
}
Run Code Online (Sandbox Code Playgroud)

因此,令人困惑的部分是如何在其他线程中看到设置owner,它只是一个普通的实例变量.实际上,读取是在调用(并且是一个合格的变量)之后,但是在设置之后,根本就没有(可以施加同步语义).数据竞争发生了?AbstractOwnableSynchronizerelse if (current == owner)ownergetState()statevolatileAQSowner

好吧,鉴于本书的权威性和经过彻底测试的代码,我想到了两种可能性:

  1. 设置之前的完整屏障(无论是mfence还是'lock'ed指令)owner = current执行隐藏的工作.但是从我从几篇着名的文章中学到的,完整的屏障更关心它之前的写作以及它之后的读取.好吧,如果这种可能性成立,那么"JCIP"中的一些句子可能会被不适当地陈述.

  2. 我注意到'地理位置' setState(c+1)确实owner = current在代码片段中出现,尽管它位于if-else的另一个分支中.如果评论说的是事实,是否意味着插入的障碍setSate(c+1)可以owner = current在另一个分支中强加同步语义?

我是这方面的新手,几个很棒的博客帮助我理解了JVM的基础(没有排序):

以及永远的宏伟:http://g.oswego.edu/dl/jmm/cookbook.html

做完作业和搜索互联网后,我得不到令人满意的结论.

请原谅我,如果这太冗长或不清楚(英语不是我的母语).请帮助我,相关的任何事情表示赞赏.

ewe*_*nli 3

owner = current;您怀疑(在 CAS 之后)和if (current == owner)(在读取状态并检查状态是否 >0 之后)之间可能存在竞争。

单独考虑这段代码,我认为你的推理是正确的。tryRelease但是,您还需要考虑:

 123:         protected final boolean tryRelease(int releases) {
 124:             int c = getState() - releases;
 125:             if (Thread.currentThread() != getExclusiveOwnerThread())
 126:                 throw new IllegalMonitorStateException();
 127:             boolean free = false;
 128:             if (c == 0) {
 129:                 free = true;
 130:                 setExclusiveOwnerThread(null);
 131:             }
 132:             setState(c);
 133:             return free;
 134:         }
Run Code Online (Sandbox Code Playgroud)

这里所有者null在状态设置为 0 之前被设置为 。要最初获取锁,状态必须为 0,因此所有者为null

最后,

  • 如果线程if (current == owner)到达c=1
    • 它可以是拥有线程,在这种情况下,所有者是正确的并且状态会递增。
    • 它可以是另一个线程,它可以看到或看不到新的所有者。
      • 如果它看到了,一切都很好。
      • 如果没有,它会看到null,这也很好。
  • 如果线程if (current == owner)到达c>1
    • 它可以是拥有线程,在这种情况下,所有者是正确的并且状态会递增。
    • 它可以是另一个线程,但所有者肯定是正确的。

我同意 JCIP 中的脚注“仅在调用 getState 后读取所有者字段并仅在调用 setState 之前写入它”具有误导性。它写了ownerbefore 调用setStatetryRelease但没有tryAcquire