7

クラスアイテムがあります

class Item {
  public int count;
  public Item(int count) {
    this.count = count;
  }
}

次に、アイテムへの参照を他のクラスのフィールドに入れます

class Holder {
  public Item item;
  public Holder() {
    item = new Item(50);
  }
}

この新しい Itemオブジェクトは安全に公開できますか? そうでない場合、なぜですか?Java Concurrency in Practice によると、新しいアイテムは完全に構築されずに公開されますが、私の意見では、新しいアイテムは完全に構築されています。そのthis参照はエスケープされず、それへの参照とその状態は同時に公開されるため、コンシューマ スレッドは古い値を認識しません。それとも視認性の問題ですか?理由はよくわかりません。

4

2 に答える 2

16

この新しい Item オブジェクトは安全に公開できますか? そうでない場合、なぜですか?

この問題は、命令の最適化と並べ替えに関係しています。同期せずに構築されたオブジェクトを使用している 2 つのスレッドがある場合、コンパイラが効率のために命令の順序を変更し、オブジェクトにメモリ空間を割り当て、その参照をitemフィールド格納してから、コンストラクタとフィールドの初期化。または、メモリ同期を並べ替えて、他のスレッドがそのように認識できるようにすることもできます。

itemフィールドを としてマークするfinalと、コンストラクターはコンストラクターの一部としてそのフィールドの初期化を完了することが保証されます。それ以外の場合は、ロックを使用する前にロックを同期する必要があります。これはJava 言語定義の一部です。

ここに別のカップルの参照があります:

于 2012-04-24T15:33:21.530 に答える
6

Holder.itemはい、そうではないので、可視性の問題がありfinalます。そのため、 の構築が終了した後、別のスレッドがその初期化された値を参照するという保証はありませんHolder

また、@Gray が指摘したように、JVM はメモリ バリア (ロック (同期)、finalまたはvolatile修飾子によって作成される可能性がある) がない場合、命令を自由に並べ替えることができます。コンテキストに応じて、安全な公開を確保するために、ここでこれらのいずれかを使用する必要があります。

于 2012-04-24T15:33:33.993 に答える