Java的AQS.Node源码疑惑

发布于 2022-09-02 19:38:27 字数 579 浏览 11 评论 0

AbstractQueuedSynchronizerNode内部类中,对volatile Node prev成员变量获取方法predecessor()如下

   
    final Node predecessor() throws NullPointerException {
        Node p = prev;
        if (p == null)
            throw new NullPointerException();
        else
            return p;
    }

在源码中,这里对volatile类型的成员变量prev的返回,是先把他赋值给一个中间变量p,然后拿p返回。
这种设计在AQS的源码中很多地方都有涉及到,包括在其它源码中也经常看到对volatile类型的变量先赋值给另外一个变量,然后把这个变量返回.
这样设计的目的是什么?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

画尸师 2022-09-09 19:38:27
// Works with acquire/release semantics for volatile in Java 1.5 and later 
// Broken under Java 1.4 and earlier semantics for volatile 
class Foo {
    private volatile Helper helper;
    public Helper getHelper() {
        Helper result = helper;
        if (result == null) {
            synchronized(this) {
                result = helper;
                if (result == null) {
                    helper = result = new Helper();
                }
            }
        }
        return result;
    }
// other functions and members... }

Note the local variable result, which seems unnecessary. The effect of this is that in cases where helper is already initialized (i.e., most of the time), the volatile field is only accessed once (due to "return result;" instead of "return helper;"), which can improve the method's overall performance by as much as 25 percent.[6]

If the helper object is static (one per class loader), an alternative is the initialization on demand holder idiom[7] (See Listing 16.6[8] from the previously cited text.)

-------Wikipedia

Oo萌小芽oO 2022-09-09 19:38:27

predecessor()这个方法里,Node p的效果不那么明显。请允许我把例子变得更极端一些:

final Node extremePredecessor() throws NullPointerException {
    // #L0: Node p = prev;
    // #L1
    if (crazyConditional_1(prev))  {
      ...
    }
    // #L2
    else if (crazyConditional_2(prev))  {
      ...
    }        
    // #L3
    else if (crazyConditional_3(prev)) {
      ...
    }
    // #L4
    else {
      return prev;
    }
}

假定有100个threads调用会改动prev的值,则在#L1到#L4之间,任何对于shared variable -- prev的改动都对extremePredecessor()是可见的。
这会有以下问题:

  • 和同步锁很类似,对prev的同步更新,会造成性能损耗,prev就成了整个Queue就有了bottleneck。

  • 在#L1到#L4之间的prev的值可能是inconsistent的,因为别的thread改动了他。这使得理解code的难度大大增加。

如果使用Node p = prev;那么#L0之后,就不需要同步p的值了。#L1到#L4的p也是consistent的。

对于volatile,请参见:
Java Language Spec volatile keyword
https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文