14

final 以外のフィールドのオブジェクト初期化が不十分な場合にメモリの可視性の問題を再現しようとしています (JLS 17.5 Final Field SemanticsFinalFieldExampleクラスの例)。「ただし、fy は最終的なものではありません。したがって、reader() メソッドは値 4 を表示することが保証されていません」

私はこのコードを試しました:

public class ReorderingTest2 {


    public static void main(String[] args) {
        for (int i = 0; i < 2500; i++) {
            new Thread(new Reader(i)).start();
            new Thread(new Writer(i)).start();
        }
    }

    static class Reader implements Runnable {
        private String name;

        Reader(int i) {
            this.name = "reader" + i;
        }

        @Override
        public void run() {
            //System.out.println(name + " started");
            while (true) {
                FinalFieldExample.reader(name);
            }
        }
    }

    static class Writer implements Runnable {
        private String name;

        Writer(int i) {
            this.name = "writer" + i;
        }

        @Override
        public void run() {
            //System.out.println(name + " started");
            while (true) {
                FinalFieldExample.writer();
            }
        }
    }

    static class FinalFieldExample {
        int x;
        int y;
        static FinalFieldExample f;

        public FinalFieldExample() {
            x = 3;
            y = 4;
        }

        static void writer() {
            f = new FinalFieldExample();
        }

        static void reader(String name) {
            if (f != null) {
                int i = f.x;
                int j = f.y;
                if (i != 3 || j != 4) {
                    System.out.printf("reader %s sees it!%n", name);
                }
            }
        }
    }

}

以前の同様のトピックと同様に、Windows を搭載したさまざまな PC (2 コアから 8 コアまで) と、サーバー側の Solaris 32 コア ボックスでさえ試しましたが、再現できませんでした: fx と fy は常に適切です。 -初期化。

私が答えを得たIntel/x86/x64アーキテクチャの場合、そのようなコンストラクタロジックの並べ替えを防ぐデフォルトのメモリ保証がほとんどあります。Solaris/sparc でも同じことが言えますか?

では、どのアーキテクチャ/OS でこの並べ替えを再現できるでしょうか?

4

4 に答える 4

1

アルファ。ポールE.マッケニーの本は並列プログラミングが難しいです、そしてもしそうなら、あなたはそれについて何ができますか?最も重要なプラットフォームのメモリモデルを説明する章があります。

于 2011-06-11T08:32:33.760 に答える
0

これはおそらく別の質問になるはずです...しかし、それは非常に的を射ています。これは、私が以前に行ったコメントのより広範なバージョンです。

jls のセクション 17.4 の最初の部分は次のように述べています。

実行中のスレッド t のアクションが正当であるかどうかを判断するには、この仕様の残りの部分で定義されているように、単一のスレッド化されたコンテキストで実行されるスレッド t の実装を評価するだけです。

私がつまずいたのは、プログラムの順序に関して、「この仕様の残りの部分で定義されているように」が何を意味するのかを理解することです。

当面の場合、割り当て

f = new FinalFieldExample();

以下が関係する割り当てセマンティクス (セクション 15.26.1) の対象となります。仕様では紛らわしいほどフォーマットが間違っています (特に 3 番目のステップ)。意図を正確に反映するために再フォーマットしたと思います。

[それ以外の場合] 3 つの手順が必要です。

  1. 最初に、左側のオペランドが評価されて変数が生成されます。この評価が突然完了すると、代入式は同じ理由で突然完了します。右側のオペランドは評価されず、代入は行われません。
  2. それ以外の場合は、右側のオペランドが評価されます。この評価が突然完了すると、代入式は同じ理由で突然完了し、代入は発生しません。
  3. それ以外の場合、右側のオペランドの値は左側の変数の型に変換され、適切な標準値セット (拡張指数値セットではない) への値セット変換 (§5.1.13) を受けます。変換の結果は変数に格納されます。

これは、シングルスレッドの「プログラム順序」の仕様のように見えます。私は何を誤解していますか?

おそらく答えは、実際に意図されているのは「ダック テスト」であるということです。指定された順序ですべてが実行されたかのように単一のスレッドが実行される場合、それは正しい実装です。しかし、このセクションは、 appearという単語を使用してこれを明確にしている他の場所とは大きく異なって書かれています。

また、Java プログラミング言語は、演算子のすべてのオペランド (条件演算子 &&、||、および ? : を除く)が、操作自体の一部が実行される前に完全に評価されているように見えることも保証します。

于 2011-07-09T15:00:12.320 に答える
0

必要な結果を得るには、高度な最適化をオンにして、プログラムを-serverモードで実行してください。

私は最初に を作ろうと考えましf volatileたが、それは明らかに実験全体を台無しにしてしまいます。

ジャストインタイム コンパイラの XML ロギングをオンにし (HotSpot JVM を使用している場合)、生成されたマシン コードを確認します (外部デバッガまたはメモリ ダンパーを使用)。次に、生成されたコードを調べて、必要な結果を観察できるかどうかを確認できます。

于 2011-06-11T06:51:41.497 に答える
0

「Java Concurrency in Practice」のコピーを入手し、ロックと可視性に関する JVM の保証について詳しく説明している第 3 章を読むことをお勧めします。あなたの質問は特定のアーキテクチャとは関係なく、Java での事前発生を理解することとすべて関係があります。

x = 3 および y = 4 であることを保証する FinalFieldExample コンストラクターの最後に発生する前のエッジがあるため、問題を再現できないと思います。

ところで。FinalFieldExample オブジェクトは少し混乱しています。適切なシングルトンになりたいのですが、そのようにコーディングしていません。静的な "f" に関する同期の欠如により、このクラスの実行時の動作を推測することが本来よりも難しくなっています。静的な「f」へのアクセスを保護する同期を備えた適切なシングルトンである必要があり、次のようなライターおよびリーダーメソッドを呼び出す必要があると思います...

FinalFieldExample.getInstance().writer();

言ってるだけ'

于 2011-06-23T19:37:28.093 に答える