2

ではJava Concurrency In Practice、不変クラスを作成する方法を説明するために、次の例を示します。

http://www.javaconcurrencyinpractice.com/listings/ThreeStooges.java

このクラスには次private final Set<String> stooges = new HashSet<String>();のものがあります。コンストラクターで初期化されます。

public ThreeStooges() {
    stooges.add("Moe");
    stooges.add("Larry");
    stooges.add("Curly");
}

と方法があります

public boolean isStooge(String name) {
    return stooges.contains(name);
}

名前が三ばか大将の1つであるかどうかを確認します。

しかし、私がこれを行うとき: 、その参照がに設定される前にThreeStooges ts = new ThreeStooges()、オブジェクトが適切に構築される(つまり、正しく初期化された状態)ことが保証されていますか?stoogests

言い換えると、このオブジェクトを公開した場合、一部のスレッドがそれを誤って初期化されたと見なす可能性がありますか(つまり、をstooges介してアクセスすると空と見なされますisStooge())?

私の理解では、不変オブジェクトは適切に構築され、公開されたときに正しく表示されます-(最終的なインスタンス変数を使用するため)。私の理解は正しいですか?はいの場合、このクラスはまだ不変ですか?

編集:私が見たコメントから、コンストラクターが完了する前にオブジェクトが他のスレッドによって見られるとは信じがたいようです。これに関するリンクは次のとおりです。http://jeremymanson.blogspot.in/2008/05/double-checked-locking.html

4

4 に答える 4

10

ここにはたくさんの間違った答えがあります:(

Java メモリ モデルの final フィールドに対する初期化の安全性の保証は、驚くほど強力です。コンストラクターの最終フィールドへの書き込みが、オブジェクトへの共有参照を取得するすべてのスレッドに見えることを保証するだけでなく (その参照がデータ競合を介して取得された場合でも)、コンストラクターへのすべての書き込みがそれを介して行われることを保証します。参照は、その参照を介して読み取りに表示されます。唯一の注意点は、構築中のオブジェクトへの参照が構築中にエスケープされないことです。もちろん、クラスが構築後にオブジェクトを変更する場合、またはクライアントが HashSet へのオブジェクト参照を取得する方法を提供する場合、すべての賭けはオフになります。

この保証の目的は、不変 (この場合は実質的に不変) オブジェクトの状態について、トリッキーな推論を行う必要がないようにすることです。フィールドが final であり、コンストラクター以外の参照先オブジェクトの状態への書き込みがない場合は、完了です。

これで頭が痛くなっても、心配しないでください。(a) 参照フィールドを非公開かつ最終的なものにし、(b) コンストラクターの外部で参照先オブジェクトの状態を変更せず、(c) クライアントが同じことを行えるようにするアクセスを提供しない場合 (例:ミュータティブ メソッド、フィールドを公開する getter などはありません)、これで完了です。

于 2012-10-28T15:33:20.460 に答える
2

オブジェクトが完全に初期化される前に、スレッドがオブジェクトにアクセスする可能性はありません。コンストラクターは、まったく新しい参照を返す通常の関数と見なすことができます。参照は新しいため、初期化前に他のスレッド (作成スレッド以外) がアクセスしているとは思いません。

于 2012-10-28T14:40:22.633 に答える
1

セットを変更できる関数を公開しないため、このクラスは不変ですstooges

stoogesはfinalであるため、構築後に完全に初期化されることが保証されています。この投稿では、次のルールが引用され、説明されています。

オブジェクトが完全に初期化された後でのみオブジェクトへの参照を表示できるスレッドは、そのオブジェクトの最終フィールドの正しく初期化された値を表示することが保証されます。

エントリは構築に追加され、後で要素は追加されないため、すべてのスレッドがセットを正しく表示できる必要がありますstooges

于 2012-10-28T14:36:05.087 に答える
0
private final Set<String> stooges = new HashSet<String>();

public ThreeStooges() {
    stooges.add("Moe");
    stooges.add("Larry");
    stooges.add("Curly");
}

stoogesセットが宣言されているため、このコードは不変ですfinal。ただし、ここではクラスのみが不変であり、要素は同じクラスによっていつでも追加できるため、セットではありません。ここでは、セットは 1 回しか作成できず、変更することはできません。references

可視性の場合、セットが であるため、それらは可視ではありませんprivate。にするとpublic、監視する前にオブジェクトを構築する必要があるため、それらは構築後にのみ表示されます。

于 2012-10-28T14:39:03.133 に答える