0

いくつかの記事で、ダブルチェックのロックが壊れていると読みました。コンパイラーはコンストラクターのシーケンスを並べ替えることができるため。

  1. オブジェクトにメモリを割り当てます
  2. 次に、アドレスを参照変数に戻します
  3. 次に、オブジェクトの状態を初期化します

通常、次のことが期待されます。

  1. オブジェクトに割り当てられたメモリと同じにする必要があります
  2. 次に、オブジェクトの状態を初期化します
  3. 次に、参照変数にアドレスを返します。

繰り返しになりますが、synchronizedキーワードを使用すると、JMM 仕様に従ってコードの並べ替えが発生することはありません。

Synchronized() ブロック内にあるときに、コンパイラがコンストラクター イベントのシーケンスを並べ替えるのはなぜですか?

ここで DCL に関する多くの投稿を見ましたが、JMM とコンパイラの並べ替えに基づく説明を期待しています。

4

3 に答える 3

3

コンパイラは、同期ブロック内の命令を自由に並べ替えることができます。また、コンパイラは、同期ブロックの(前にある限り) または後(後にある限り) 命令を自由に並べ替えることができます。ただし、コンパイラは、同期されたブロックの境界 (ブロックの開始またはブロックの終了) を越えて命令を自由に並べ替えることができません。

したがって、完全に同期ブロック内にある構築と割り当てを並べ替えることができ、正しく同期化されていない外部の視聴者は、構築前の割り当てを見ることができます。

于 2013-08-06T03:42:27.553 に答える
0

Synchronized() ブロック内にあるときに、コンパイラがコンストラクター イベントのシーケンスを並べ替えるのはなぜですか?

これは通常、コードの実行を高速化するために行われます。

Java 言語仕様 (JLS) によると、実装 (コンパイラなど) は、特定の制約に従って、命令および命令のシーケンスを並べ替えることが許可されています。

問題は、DCL の壊れたバリアントが、JLS が行うことができると言っているものから外れた仮定を行うことです。その結果、JLS が整形式ではないと言う実行が行われます。これが実際のバグ/予期しない動作として現れるかどうかは、コンパイラのバージョン、ハードウェア、およびその他のさまざまなものに依存します。

しかし重要なのは、コンパイラが何も間違ったことをしていないということです。障害は DCL コードにあります。


JIT コンパイラーは、多くの場合、イベント自体を並べ替えないことを付け加えたいと思います。多くの場合、ハードウェア レベルのメモリの読み取り/書き込みアクションに対する制約を取り除きます。たとえば、特定のメモリ書き込みがメイン メモリにフラッシュされるという制約を取り除くことで、ハードウェアが低速なメモリへの書き込みを延期 (または完全にスキップ) し、L1 キャッシュに書き込むだけにすることができます。対照的に、synchronizedブロックの最後では、キャッシュされた書き込みがメイン メモリに強制的に書き込まれ、余分なメモリ トラフィックが発生し、(おそらく) パイプライン ストールが発生します。

于 2013-08-06T04:32:22.763 に答える
0

初めに:

この場合も、synchronized キーワードを使用すると、JMM 仕様に従ってコードの並べ替えが発生することはありません。

上記の記述は完全に正確ではありません。JMM は、事前発生の関係を定義しました。JLS は、プログラムの順序と先行発生順序のみを定義します。17.4.5を参照してください。ご注文前に発生します。

命令の並べ替えに影響します。例えば、

int x = 1;
synch(obj) {
    y = 2;
}
int z = 3;

上記のコードでは、以下のタイプの並べ替えが可能です。

synch(obj) {
    int x = 1;
    y = 2;
    int z = 3;
}

上記は有効な並べ替えです。

Roach Motels と Java メモリ モデルを参照してください。

synch(obj) {
    int z = 3;
    y = 2;
    int x = 1;
}

上記も有効な並べ替えです。

不可能なことは、ロックが取得された後、ロックが解放される前にのみ y=2 が実行されるということです。これは、JMM によって保証されています。また、別のスレッドからの正しい効果を確認するには、同期ブロック内でのみ y にアクセスする必要があります。

今、私はDCLに来ています。

DCL のコードを参照してください。

if (singleton == null)
    synch(obj) {
        if(singleton == null) {
            singleton == new Singleton()
        }
    }
return singleton;

上記のアプローチの問題は次のとおりです。

  1. singleton = new Singleton()単一の命令ではありません。しかし、一連の指示。コンストラクターを完全に初期化する前に、最初にシングルトン参照にオブジェクト参照が割り当てられる可能性は十分にあります。

  2. したがって、1が発生した場合、他のスレッドがシングルトン参照を非 null として読み取り、部分的に構築されたオブジェクトを参照している可能性が十分にあります。

上記の効果は、シングルトンを volatile にすることで制御できます。これにより、事前発生の保証と可視性も確立されます。

于 2013-08-06T07:08:26.293 に答える