6

JCIP のセクション 16.3「初期化の安全性」の説明をいくつか読みましたが、まだ明確ではありません。セクションは次のように述べています

「さらに、適切に構築されたオブジェクトの final フィールド (final 配列の要素や final フィールドによって参照される HashMap の内容など) を介して到達できる変数は、他のスレッドからも見えることが保証されています。」

したがって、次の可変オブジェクトがあるとします。

public final class Container{
    private String name;
    private int cupsWon;
    private double netWorth;

        public Container( String name, int cupsWon, double netWorth ){
             this.name = name;
             this.cupsWon = cupsWon;
             this.netWorth = netWorth;
        }

    //NO Setters
    //Getters
}

次に、スレッド 1は次のように作成し、 c をThread2に渡します。

final Container c = new Container("Ted Dibiasi", 10, 1000000);

Thread2 (同時にではなく、1 ミリ秒後としましょう)、c の値を読み取ります。Thread2 が参照する可能性はありますか

c.name=null or
c.cupswon=0 or worst of all, 
c.netWorth=0.0?

乾杯

アップデート

クラスにゲッターがあることに混乱があることに気付きました。ソースコードを更新しています。これが明確になることを願っています。ご覧いただきありがとうございます。

public final class Container{

    private String name;
    private int cupsWon;
    private double netWorth;

    public Container( String name, int cupsWon, double netWorth ){
        this.name = name;
        this.cupsWon = cupsWon;
        this.netWorth = netWorth;
    }

    public final String getName(){
        return name;
    }

    public final int getCupsWon(){
        return cupsWon;
    }

    public final double getNetWorth(){
        return netWorth;
    }

}

//---------

public final class Producer{

    private final Client client;

    public Producer( Client client ){
         this.client = client;
    }

    //Thread1 call produce()   
    public final void produce( ){
        final Container c = new Container("Ted Dibiasi", 10, 1000000);
        client.update( c );
    }

}

//----

public final class Client{

     private Container c;
     //private volatile Container c;       

     public final void update( Container c ){
          this.c = c;
     }

     //Thread2 calls consume().
     public final void consume( ){
          String name = c.getName();
          int cupsWon = c.getCupsWon();
          double netWorth = c.getNetWorth();           
     }

 }

私の質問は次のとおりです。

a) Thread2が consumer () を呼び出すとき、name、cupsWon、netWorth は null、0、または 0.0 になる可能性がありますか? Container クラスのフィールドは最終的なものではないため、可視性が保証されないため、可能であると考えていました。

b) ただし、セクション 16.3 と「適切に構築されたオブジェクトの final フィールドを介して到達できる変数」に関するビットを読みましたが、これは、コンテナ c のインスタンスが final と宣言されているため、可視性が保証されていることを意味します?消費()?

最終的なコンテナ c = 新しいコンテナ("Ted Dibiasi", 10, 1000000);

c) Client クラスの Container への参照を volatile として宣言しても、参照に関連するため、フィールドの可視性の問題は解決されません。

4

3 に答える 3

6
final Container c = new Container("Ted Dibiasi", 10, 1000000);

cここに最終フィールドがあり、ローカル変数ではない場合、 Java 言語仕様Thread1からの引用がこの最終フィールドに適用されます。c

コンストラクターが終了すると、オブジェクトは完全に初期化されたと見なされます。オブジェクトが完全に初期化された後にのみオブジェクトへの参照を確認できるスレッドは、そのオブジェクトの final フィールドの正しく初期化された値を確認できることが保証されます

final フィールドの使用モデルは単純です。オブジェクトのコンストラクタでオブジェクトの final フィールドを設定します。オブジェクトのコンストラクターが終了する前に、別のスレッドが参照できる場所に構築中のオブジェクトへの参照を書き込まないでください。これに従えば、オブジェクトが別のスレッドから見られるとき、そのスレッドは常に、そのオブジェクトの final フィールドの正しく構築されたバージョンを認識します。また、少なくとも最終フィールドと同じくらい最新の最終フィールドによって参照されるオブジェクトまたは配列のバージョンも表示されます。

ここでの言い回しは曖昧ですが、「正しく初期化された値」と「最終フィールドとして最新」は、コンストラクターcThread2外側にを渡すと、完全に構築されたインスタンスとそのフィールドが常に表示されることを意味すると思います初期化されました。Thread1Thread2Container

于 2015-09-08T19:46:34.817 に答える
0

あなたの質問に答えるには、いいえ、Thread2 は Container のフィールドを初期化されていない状態で見ることはありません。その理由は 、参照がアクセス可能になる前に Container のコンストラクターが完全に実行されるためです。呼び出しとContainer cの間で競合状態が発生している可能性があります。この競合の結果に応じて、フィールドは null になるか、 での呼び出し時に完全に初期化された Container オブジェクトになります。最初のケースでは NullPointerException を取得し、2 番目のケースでは正しく初期化された値を取得します。JCIPの引用文とは関係ないと思います。Client.updateClient.consumecc.getName()Client.consume

于 2015-09-21T13:55:31.857 に答える
0

通常、プログラマはこの問題について心配する必要はありません。オブジェクトが「安全でない状態で発行された」場合にのみ問題になります。たとえば、オブジェクトがスレッド 1 によって不揮発性静的フィールドに割り当てられ、スレッド 2 が不揮発性フィールドの読み取りからオブジェクトを取得します。ただし、これはめったにありません。オブジェクトは、ほとんどの場合、何らかのメモリ バリアを使用してスレッド間で渡されます。たとえば、オブジェクトを ThreadPoolExecutor に渡す場合、可視性について心配する必要はありません。

あなたが本当にそれを必要とし、あなたが何をしているのかを正確に知っている場合を除き、危険な公開は絶対に避けるべきです.

正当な理由がない限り、通常、安全でないパブリケーションに耐えるようにクラスを設計する必要はありません。たとえば、Stringこのように設計されているのは、コア セキュリティ/アクセス制御コードで広く使用されており、一部の悪意のあるプログラムが安全でない公開によって文字列を妨害しようとしても、文字列の内容が一定であるように見える必要があるためです。

final安全でないパブリケーションに耐えるために、ほとんどのクラスはフィールドを使用する必要はありません。

于 2015-09-08T20:23:22.630 に答える