首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ReentrantLock.Sync中当前线程变量的支持是如何工作的?

ReentrantLock.Sync中当前线程变量的支持是如何工作的?
EN

Stack Overflow用户
提问于 2013-09-11 03:17:25
回答 2查看 1.2K关注 0票数 9

我在第14.6.1节中读到了在"Java in Practice“中实现ReentrantLock的一些细节,注释中的一些内容让我感到困惑:

由于受保护的状态操作方法具有易失性读或写的内存语义,而且ReentrantLock只在调用getState和只在调用setState之前才仔细读取所有者字段,所以ReentrantLock可以在同步状态的内存语义上进行调整,从而避免进一步的同步(参见第16.1.4节)。

它所指的守则:

代码语言:javascript
复制
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;
}

我相信这是nonfairTryAcquireReentrantLock.Sync中的简化代码。

代码语言:javascript
复制
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;
}

因此,令人费解的是owner的设置(它只是AbstractOwnableSynchronizer中的一个普通实例变量)如何在其他线程中为else if (current == owner)所可见。实际上,owner的读取是在调用getState()之后( stateAQSvolatile限定变量),但是在owner设置之后,根本就没有任何东西(可以强制执行同步语义)。会发生数据竞争吗?

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

  1. 设置owner = current之前的全部障碍(不管是mfence还是‘lock’‘ed指令)做了隐藏的工作。但我从几篇著名的文章中了解到,完全障碍更关心前面的书写以及后面的阅读。好吧,如果这种可能性成立,那么"JCIP“中的一些句子可能会被错误地表述。
  2. 我注意到,在代码段中,“地理上”的setState(c+1)实际上是在owner = current之后,尽管它位于if-else的另一个分支中。如果评论说的是事实,这是否意味着setSate(c+1)插入的屏障可以将同步语义强加给另一个分支中的owner = current

我是这个领域的新手,几个伟大的博客帮助我理解JVM的基础(没有排序):

  • 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

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

如果这句话太冗长或含糊不清,请原谅(英语不是我的母语)。请帮我做这件事,任何相关的东西我们都很感激。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-09-12 07:25:21

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

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

代码语言:javascript
复制
 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

因此,

  • 如果一个线程使用c=1到达c=1,则
    • 它可以是拥有线程,在这种情况下,所有者是正确的,状态增加。
    • 它可以是另一个线程,它可以看到或看不到新的所有者。
      • 如果它看到了,一切都很好。
      • 如果没有,它将看到null,这也很好。

  • 如果一个线程使用c>1到达c>1,则
    • 它可以是拥有线程,在这种情况下,所有者是正确的,状态增加。
    • 它可以是另一个线程,但是所有者肯定是正确的。

我感到愤慨的是,JCIP中的脚注“只在调用getState之后才读取所有者字段,而只在调用setState之前才写它”是有误导性的。它在调用owner之前编写tryRelease,而不是tryAcquire

票数 3
EN

Stack Overflow用户

发布于 2013-09-11 05:07:53

这一点在这篇博客文章中解释得很好。底线是,当读取线程读取易失性字段时,在写入易失性字段之前修改了的所有写入线程更新的字段对读取线程也是可见的。锁类组织字段访问,以确保只有状态字段需要易失性,并且在需要时所有者字段仍然是安全的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18732088

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档