Java ReentrantLock.Sync中当前线程变量的装载是如何工作的?

Java ReentrantLock.Sync中当前线程变量的装载是如何工作的?,java,multithreading,concurrency,synchronization,memory-barriers,Java,Multithreading,Concurrency,Synchronization,Memory Barriers,我在14.6.1节的“Java并发实践”中读到了一些关于ReentrantLock实现的细节,注释中的一些内容让我感到困惑: 由于受保护状态操作方法具有易失性读或写的内存语义,并且ReentrantLock仅在调用getState之后读取所有者字段,并且在调用setState之前写入所有者字段,因此ReentrantLock可以利用同步状态的内存语义,从而避免进一步同步,见第16.1.4节 它所指的代码: protected boolean tryAcquire(int ignored) {

我在14.6.1节的“Java并发实践”中读到了一些关于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;
}
我相信这是
ReentrantLock.Sync
nonfairTryAcquire
的简化代码

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;
}
最终布尔非空获取(int获取){
最终螺纹电流=Thread.currentThread();
int c=getState();
如果(c==0){
if(比较设置状态(0,获取)){
设置ExclusiveOwnerThread(当前);
返回true;
}
}
else if(当前==getExclusiveOwnerThread()){
int nextc=c+收购;
if(nextc<0)//溢出
抛出新错误(“超过最大锁计数”);
设置状态(nextc);
返回true;
}
返回false;
}
因此,令人困惑的部分是,如果在其他线程中(current==owner),则
所有者的设置(它只是
AbstractOwnableSynchronizer
中的一个普通实例变量)如何对
可见。实际上,
owner
的读取是在调用
getState()
之后进行的(并且
state
AQS
volatile
限定变量),但是在设置
owner
之后,根本没有任何东西(可以施加同步语义)。数据竞赛发生了吗

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

  • 设置
    owner=current
    之前的完整屏障(无论是mfence还是“锁定”指令)执行隐藏操作。但从我从几篇著名的文章中了解到,完整的屏障更关注它之前的书写以及之后的阅读。好吧,如果这种可能性成立,那么“JCIP”中的一些句子可能会被不恰当地表述

  • 我注意到,
    setState(c+1)
    在代码片段中真正位于
    owner=current
    之后,尽管它位于if-else的另一个分支中。如果评论所说的是事实,是否意味着
    setstate(c+1)
    插入的障碍可以将同步语义强加给另一个分支中的
    owner=current

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

    以及永远华丽的:

    在做完家庭作业和上网后,我没有得出令人满意的结论


    请原谅,如果这太冗长或不清楚(英语不是我的母语)。请帮我做这件事,任何相关的事情都很感激。

    这在中解释得很好。底线是,当读取线程读取volatile字段时,写入线程在写入volatile字段之前修改的所有字段也将对读取线程可见。lock类组织字段访问,以确保只有state字段需要易变,并且在需要时所有者字段仍然安全地分配

    您怀疑
    owner=current之间可能存在竞争(在CAS之后)和
    如果(当前==所有者)
    (在读取状态并检查其是否>0之后)

    单独来看这段代码,我认为您的推理是正确的。但是,你也需要考虑<代码> TryPrave<代码>:

     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:         }
    
    在这里,在状态设置为0之前,所有者被设置为
    null
    。要最初获取锁,状态必须为0,因此所有者为
    null

    因此,

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

    我认为JCIP中的脚注“仅在调用getState后读取所有者字段,而仅在调用setState之前写入所有者字段”具有误导性。它在调用
    tryRelease
    中的
    setState
    之前写入
    owner
    ,但不写入
    tryAcquire

    这正是让我困惑的地方:在CAS操作之后,所有者字段被更新为volatile字段。所谓的背驮在这里似乎不起作用。不过,感谢您的链接,另一个链接进入了集合。是的,但所有者字段的内容唯一重要的时候是在我们知道c!=0,因此我们也知道我们是拥有线程,因此内存模型中变量的状态不会有问题(如果我们不是拥有线程,那么
    owner==thread.currentThread()
    我明白了。你的解释很有道理。可能是我太专注于栅栏的事情了,这只是缩小了我的视野,忽略了逻辑上的原因。但是说到我上面的问题,你能帮我得出一个结论吗