如何在ReentrantLock.Sync中当前线程变量的搭载工作? [英] How does the piggybacking of current thread variable in ReentrantLock.Sync work?

查看:133
本文介绍了如何在ReentrantLock.Sync中当前线程变量的搭载工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我阅读了ReentrantLock在Java Concurrency in Practice第14.6.1节中的一些实现细节,注释中的某些内容使我困惑:

I read about some of the details of implementation of ReentrantLock in "Java Concurrency in Practice", section 14.6.1, and something in the annotation makes me confused:


由于受保护的状态操作方法具有易失性读或写的内存语义,并且ReentrantLock小心只有在调用getState 和 write之后才<它只有在调用setState 之前,ReentrantLock可以捎带在同步状态的内存语义上,从而避免进一步的同步,见第16.1.4节。

Because the protected state-manipulation methods have the memory semantics of a volatile read or write and ReentrantLock is careful to read the owner field only after calling getState and write it only before calling setState, ReentrantLock can piggyback on the memory semantics of the synchronization state, and thus avoid further synchronizationsee Section 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;
     }
}

我相信这是简化的< ReentrantLock.Sync 中的code> nonfirTryAcquire 。

And I believe this is the simplified code of the nonfirTryAcquire in the ReentrantLock.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;
}

所以,令人不解的部分是如何设置 中的一个普通的实例变量,变为可见if(current == owner)在其他线程。事实上,读取 owner 是在调用 getState()(和状态是 AQS volatile 限定变量),但在 owner ,没有什么(可以强加同步语义)。数据竞争是否发生?

So, the baffling part is how the setting of owner, which is merely a plain instance variable in AbstractOwnableSynchronizer, becomes visible to else if (current == owner) in other threads. Indeed, the read of owner is after the calling of getState() (and the state is a volatile qualified variable of AQS), but after the setting of owner, there's nothing (can impose synchronization semantics) at all. Data race happens?

根据这本书的权威和彻底测试的代码,我想到了两种可能性:

Well, in light of the authority of this book and the thoroughly tested code, two possibilities come to my mind:


  1. 在设置 owner = current 之前的完整障碍(无论是mfence还是lock'ed指令)做隐藏的工作。但是从我从几个着名的文章中学到的,完全的屏障更多地关心之前的写作以及它之后的读取。

  1. The full barrier (be it mfence or 'lock'ed instruction) before the setting owner = current does the hidden work. But from I've learned from several famous articles, the full barrier cares more about the writes before it as well as the reads after it. Well, if this possibility holds true, then some sentences in "JCIP" might be inappropriately stated.

我注意到地理上的 setState(c + 1)真的来自代码片段中的 owner = current ,但它位于if-else的另一个分支中。如果注释说的是真的,是否意味着 setSate(c + 1)插入的屏障可以在 owner = current上强加同步语义

I notice that 'geographically' the setState(c+1) really comes after owner = current in the code snippet, although it's in another branch of if-else. If what the comments says is the truth, does it mean that the barrier inserted by setSate(c+1) can impose synchronization semantics on owner = current in another branch?

我是这个地区的新手,博客帮助我了解JVM底层的内容(无顺序):

I'm a novice in this area, and several great blogs help me a lot in understanding what's underlying the JVM(no ordering):

  • http://mechanical-sympathy.blogspot.com/
  • http://preshing.com/
  • http://bartoszmilewski.com
  • http://psy-lob-saw.blogspot.com/

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

在做家庭作业和搜索互联网后,我无法来

After doing my homework and searching the internet, I fail to come to a satisfying conclusion.

如果这句话太冗长或不清楚(英语不是我的母语),请原谅我。

Pardon me if this is too wordy or unclear(English is not my mother tongue). Please help me with this, anything related is appreciated.

推荐答案

你怀疑 owner = current; (在CAS之后)和 if(current == owner)(读取状态并检查是否> 。

You suspect there could be a race between owner = current; (after the CAS) and if (current == owner) (after reading the state and checking if it is >0).

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

Taking this piece of code in isolation, I think your reasoning is correct. However, you need to consider tryRelease as well:

 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:         }



此处,所有者设置为 null 在状态设置为0.要初始获取锁,状态必须为0,因此所有者是 null

Here the owner is set to null before the state is set to 0. To initially acquire the lock, the state must be 0, and so the owner is null.

因此,


  • 如果一个线程到达 if (current == owner) c = 1

    • 线程,在这种情况下,所有者是正确的,状态增加。

    • 它可以是另一个线程,可以看到或不是新的所有者。

      • 如果它看到,一切都很好。

      • 如果没有,它会看到 null ,这也很好。

      • If a thread reaches if (current == owner) with c=1,
        • it can be the owning thread, in which case the owner is correct and the state is incremented.
        • it can be another thread, which can see or not the new owner.
          • If it sees it, everything is fine.
          • If not, it will see null, which is fine as well.

          • 它可以是拥有线程,其中

          • 它可以是另一个线程,但是所有者肯定是正确的。

          我认为脚注仅在调用getState后才读取所有者字段在JCIP中仅在调用setState 之前写入它是误导。在调用 tryRelease 中的 setState 之前写入 owner 不是 tryAcquire

          I aggree that the footnote "read the owner field only after calling getState and write it only before calling setState" in JCIP is misleading. It writes the owner before calling setState in tryRelease, but not tryAcquire.

          这篇关于如何在ReentrantLock.Sync中当前线程变量的搭载工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆