java - 関数名の付け方 - 関数 名 文字 列




Java Object Referenceの不適切な発行 (3)

HolderクラスはOKですが、 someClassクラスは、 holderインスタンス変数がnull initialize()の作成と呼び出しの間に、不整合な状態で表示されnull

以下の例は、Brian Goetz、Chapte 3、第3.5.1節の本 "Java Concurrency in Practice"の本です。 これは、オブジェクトの不適切な発行の例です

class someClass {
    public Holder holder;

    public void initialize() {
        holder = new Holder(42);
    }
}

public class Holder {
  private int n;
  public Holder(int n) { this.n = n; }

  public void assertSanity() {
    if (n!=n)
      throw new AssertionError("This statement is false");
  }
}

Holderは、一貫性のない状態で別のスレッドに表示され、別のスレッドが部分的に構築されたオブジェクトを監視できることが示されています。 これはどうしたらできますか? 上の例を使ってシナリオを教えてください。

また、スレッドが、フィールドを最初に読み込んだときに失効した値を表示し、次回以降に日付値が増えることがある場合があります。そのため、assertSanityがアサーションエラーをスローする可能性があります。 アサーションエラーはどのようにスローされますか?

この問題を解決する1つの方法は、変数を 'n'にすることによってホルダーを不変にすることです。 今のところ、ホルダーは免除可能ではないが効果的に不変であると仮定しよう。 このオブジェクトを安全に公開するには、ホルダーの初期化を静的にして、volatile(静的な初期化とvolatileまたは単にvolatileの両方)として宣言する必要がありますか? 何かのようなもの

public class someClass {
    public static volatile Holder holder = new Holder(42);

}

あなたの助けを前もってありがとう。


あなたが尋ねる問題は、JVMの最適化と、単純なオブジェクト作成という事実によって引き起こされます。

MyClass obj = new MyClass()

必ずしもステップで実行されるわけではありません。

  1. ヒープ上のMyClassの新しいインスタンスのメモリを予約する
  2. コンストラクタを実行して内部プロパティ値を設定する
  3. ヒープ上のアドレスに 'obj'リファレンスを設定する

いくつかの最適化目的のために、JVMは以下の手順でそれを行うことができます:

  1. ヒープ上のMyClassの新しいインスタンスのメモリを予約する
  2. ヒープ上のアドレスに 'obj'リファレンスを設定する
  3. コンストラクタを実行して内部プロパティ値を設定する

したがって、2つのスレッドがMyClassオブジェクトにアクセスしたいと考えているとします。 最初のものが作成されますが、JVMのために「最適化された」一連のステップが実行されます。 私たちが重大な問題を抱える可能性がある以上、ステップ1と2(ただし、3はしません)だけを実行する場合。 2番目のスレッドがこのオブジェクトを使用する場合(ヒープ上のメモリの予約された部分をすでに指しているためnullにはなりません)、そのプロパティは間違っていて厄介なことにつながります。

この最適化は、参照が揮発性である場合には発生しません。


public class Holder {
  private int n;
  public Holder(int n) { this.n = n; }

  public void assertSanity() {
    if (n!=n)
      throw new AssertionError("This statement is false");
  }
}

1つのスレッドがHolderインスタンスを作成し、参照をassertSanityを呼び出す別のスレッドに渡したとしassertSanity

this.n内のthis.nへの代入は、1つのスレッドで行われます。 また、別のスレッドでn読み込みが2回発生します。 ここでの唯一の先行関係は、2つの読み込みの間にあります。 代入と読み込みのいずれかを含む先験的な関係はありません。

this.n = n関係がなければ、ステートメントはさまざまな方法で並べ替えることができます。したがって、スレッド1つの観点から、 this.n = nはコンストラクターが返された後に発生する可能性があります。

これは、最初の読み取りの後、2番目のスレッドの前に2番目のスレッドで割り当てが発生しているように見え、結果が一致しないことを意味します。 finalはn finalにすることで防ぐことができます。これにより、コンストラクタが終了する前に値が代入されることが保証されます。





concurrency