0

新しい Java メモリ モデルでは、変数への書き込みは、次のスレッドが読み取る前に完了することが保証されています。

これは、このオブジェクトのメンバーである変数にも当てはまりますか?

Java メモリ モデルの場合:

http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html

例えば

public class VolatileTest {
    private volatile Map<String, Function> functionMap = new HashMap<>();

    public Function getFunction(String key) {
        Function function = this.functionMap.get(key);

        if (function == null) {
            //Is this guaranteed to be fully constructed? I don't think so.
            function = new Function(key);

            this.functionMap.put(key, function);
        }

        return function;
    }
}

上記のコードのように、functionMapvolatile にしても、このメソッドが戻る前に関数オブジェクトが完全に構築されることは保証されません。

私の考えは正しいですか?

また、このトピックについては、次の点について私の考えが正しいかどうかを確認してください。

以下のように、 への書き込みはfunctionMap、 の参照を変更する前に完了することが保証されていますfunctionMapよね? メソッドの実行にどれだけ時間がinitializeMapかかっても、他のスレッドには nullfunctionMapまたは完全に初期化されたfunctionMap?が表示されます。

public Map<String,Function> getFunctionMap (){
    Map<String, Function> tempMap = new HashMap<>();

    initalizeMap(tempMap); //fill in values

    // Above operation is guaranteed to be completed
    //   before changing the reference of this variable.
    this.functionMap = tempMap;

    // So here you will either see a null or a fully initialized map.
    // Is my understanding right?
    return this.functionMap;
}

上記を明確にするために、上記の 2 つの例はすべてマルチスレッド環境にあり、functionMap 変数は複数のスレッドによってアクセスされます。

4

3 に答える 3

1

@grumpynerd こんにちは、 new Function(key) が不変のオブジェクトである場合を除いて、メソッドの戻り後に部分的に構築される可能性があるという最初の仮定は正しいです。

あなたの2番目の仮定は間違っています。

 this.functionMap = tempMap;

    // So here you will either see a null or a fully initialized map.
    // Is my understanding right?
    return this.functionMap;

ここで、同じ volatile 変数を次から次へと読み書きしています。そして、returns ステートメントの前に initializeMap が完了すると仮定します。あなたの仮定の穴は Roach Motel Model に当てはまります。しかし、JMM では必ずしもそうではありません。JMMはRoachよりも弱いモデルです。以下の質問への回答を参照してください

コメントにならないのでこちらから更新。申し訳ありませんが、2番目の仮定も当てはまります。ただし、 this.functionMap = tempMap; を覚えておいてください。Doug lea cookbook http://g.oswego.edu/dl/jmm/cookbook.html this で説明されているように、実際にはすべてのステートメントがその前に実行され、メモリにフラッシュされたように動作する可能性があります が、これはJMM の実装方法に関するガイド。たとえば、ここのコードを見て ください

ここで、ステートメントを作成しても揮発性フェンスをメモリバリアとして使用しようとしました

fence = true; 
fence = fence;

ここで、参照を返す前にオブジェクトが完全に初期化されると仮定して、揮発性の読み取りと書き込みを行っています。しかし、上記の推論には欠陥があります。なぜなら、Java の事前発生保証ではなく、メモリ バリアの観点から考えようとしているからです。私の質問の欠点は、フィールド参照が揮発性ではないため、他のスレッドが揮発性参照を読み取っていないことです。見つかったフィールドが !- null の場合でも、FieldType の他のインスタンス フィールドに対して誤った値を読み取る可能性があります。不揮発性値を読み取る場合、Javaによる保証はありません。

したがって、ステートメントの順序を制限するという観点からは決して考えません。JLS/JMM によって保証されていることは、関係が発生する前に常に考えてください。

例えば

int カウント = 1; volatile ブール値フラグ = true;

現在、jls は、上記のコードがプログラム順に実行されると述べています。つまり、count は最初に 1 になり、次に flag = true が実行されます。

    But Not the catch above from jls http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html

        It should be noted that the presence of a happens-before relationship 
between two actions does not necessarily imply that they 
have to take place in that order in an implementation. 
If the reordering produces results consistent with a legal execution, 
it is not illegal.

        For example, the write of a default value to every 
field of an object constructed by a thread need not 
happen before the beginning of that thread, as long as no 
read ever observes that fact.

上記は、count = 1 が flag = true の後に実行される可能性があることを示しています。執行が合法的な執行である場合。しかし、私たちの観察では、count=1 が最初に実行されたようです。キーワードは観察です。(コンパイラが他のスレッド間で共有されていないフラグを見つけたと仮定すると、ここでも発生する可能性があります。これは正当である可能性があります..これは単なる仮説です)

しかし、別のスレッドで以下のステートメントを実行すると、

    if(flag == true) {
       sysout(count) // count is  guaranteed  to be 1 
       // because of volatile guarantees flag will be only true if it has been set by first thread
       // and by program order count must have been set before flag has been set to  true.
       // therefore by program order i am printing count after reading value of a 
//volatile variable flag, therefore count must be 1.
    }

flag が volatile でなかった場合、2 つのスレッド間に事前発生の関係はありません。スレッド 2 は flag == true を読み取り、0 としてカウントすることができます。これは、スレッド間で観察上の保証がないためです。

要するに、保証は観察のためのものであり、各ステートメントが実際にどのように実行されるかではありません。Program Order、Synchronization Order、および Happens Before Order の詳細を読んで、知識を共有しましょう :) http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.3

于 2013-07-03T05:17:30.607 に答える
0

変数のマーク付けはvolatile、変数値が変更され、複数のスレッドによってアクセスされる場合にのみ役立ちます。あなたfunctionMapはマップへの参照であり、この参照に新しい Map インスタンスを割り当てたくない限り、ここで volatile は役に立ちません。明確にするために、 volatile はマップ コンテンツには影響せず、マップポインターのみに影響します。

Java メソッド呼び出しは非同期だと思われているようですが、そうではありません。最初の例では、オブジェクトが完全に初期化さnew Function(key);れるまで呼び出しは返されません。Function

2番目も同じです。それinitalizeMapが新しいスレッドを開始して処理を実行しない限り、ここでは問題ありません。また、intializeMapいくつかの非同期作業を行う場合でもnew HashMap<>()、tempMap を null にすることはできず、initializeMap によって追加された値が完全にロードされない可能性があります (initalizeMap新しいスレッドを開始する場合にのみ繰り返します)。ただし、null はありません。

于 2013-06-14T08:49:08.170 に答える