ホームページ  >  に質問  >  本文

并发 - Java的AQS.Node源码疑惑

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类型的变量先赋值给另外一个变量,然后把这个变量返回.
这样设计的目的是什么?

大家讲道理大家讲道理2718日前788

全員に返信(2)返信します

  • 迷茫

    迷茫2017-04-17 18:02:30

    リーリー

    不要に見えるローカル変数の結果に注目してください。この効果は、ヘルパーが既に初期化されている場合 (つまり、ほとんどの場合)、volatile フィールドへのアクセスは 1 回だけであり (「return helper;」ではなく「return result;」であるため)、これにより、このメソッドの全体的なパフォーマンスは 25% も向上します。[6]

    ヘルパー オブジェクトが静的 (クラス ローダーごとに 1 つ) の場合、代替手段は、オンデマンドの初期化ホルダー イディオム [7] です (前に引用したテキストのリスト 16.6[8] を参照してください。)

    ------ウィキペディア

    返事
    0
  • 伊谢尔伦

    伊谢尔伦2017-04-17 18:02:30

    この方法 predecessor() では、Node p の効果はそれほど明らかではありません。もう少し極端な例にすることをお許しください:

    リーリー

    100 個のスレッド呼び出しによって prev の値が変更されると仮定すると、#L1 と #L4 の間で、共有変数への変更はすべて、extremePredecessor() に表示されます。
    これには次の問題があります:

    • は同期ロックによく似ています。prev への同期更新はキュー全体のボトルネックになります。

    • #L1 と #L4 の間の prev の値は、他のスレッドによって変更されたため、矛盾している可能性があります。これにより、コードを理解することがさらに難しくなります。

    Node p = prev;を使用する場合、#L0 の後に p の値を同期する必要はありません。 #L1 から #L4 までの p も一致しています。

    volatile については、次を参照してください:
    Java 言語仕様の揮発性キーワード
    https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls- 8.3 .1.4

    返事
    0
  • キャンセル返事