7

Thread and Locks http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5に関するJLSドキュメントを調べていました。

class FinalFieldExample { 
final int x;
int y; 
static FinalFieldExample f;

public FinalFieldExample() {
    x = 3; 
    y = 4; 
} 

static void writer() {
    f = new FinalFieldExample();
} 

static void reader() {
    if (f != null) {
        int i = f.x;  // guaranteed to see 3  
        int j = f.y;  // could see 0
    } 
} 
}

上記の例 (ex no 17.5-1) で、fy がゼロと見なされる方法について説明したセクションと混同しています。リーダー スレッドは、オブジェクト f を null として読み取ります。この場合、何も実行されません。または、何らかの参照を使用してオブジェクト f を読み取ります。オブジェクト f に参照がある場合、参照を f に割り当てることができるように複数の書き込みスレッドが実行されていても、コンストラクターはその実行を完了している必要があり、コンストラクターが実行された場合、fy は 4 と見なされます。

fy =0 はどのような条件で可能でしょうか?

ありがとう

4

4 に答える 4

5

fy =0 はどのような条件で可能でしょうか?

Java メモリー・モデルにより、JIT コンパイラーはコンストラクター外の非最終フィールドの初期化の順序を変更できます。このフィールドは final であるため、JVM によって初期化する必要がありますが、final ではありません。そのため、が割り当てられてオンに設定されている可能性がありますが、フィールドの初期化は完了していません。xyFinalFieldExamplestatic FinalFieldExample fy

17.5-1 から引用するには:

オブジェクトのコンストラクターが終了した後にライター メソッドが f を書き込むため、リーダー メソッドは fx の適切に初期化された値を確認することが保証されます。値 3 を読み取ります。ただし、fy は最終的なものではありません。したがって、reader メソッドが値 4 を表示することは保証されていません。

f.yは final ではないため、コンストラクターが終了してstatic fが割り当てられるまでに設定されているという保証はありません。したがって、作成された競合状態があり、この競合に応じて 3 または 0 としてreader表示される場合があります。y

于 2013-02-07T20:44:37.490 に答える
2

スレッドが変数に書き込み、別のスレッドがそれを読み取る場合、後で読み取りが行われたとしても、2番目のスレッドが新しい値を認識しない可能性があります。これは、たとえば、2つのスレッドが異なるプロセッサで実行されており、書き込まれた値がローカルプロセッサレジスタにキャッシュされている場合に発生する可能性があります。

Java仕様では、パフォーマンスを向上させるために、この直感的でない動作が可能になっています(これが不可能な場合、プロセッサはローカルメモリを使用できませんでした)

したがって、Javaメモリモデルの「happens-before」関係について読むときはいつでも、プログラムロジックで「物理的」が起こる前に起こるとは限らないことを覚えておいてください。たとえば、同期、揮発性変数、この場合はfinal変数を使用して、2つのスレッド間の「happens-before」関係を明示的に確立する必要があります。

于 2013-02-07T20:36:20.607 に答える
1

命令の並べ替えだけではありません。fy への書き込みが f への書き込みよりも前であっても発生する可能性があります。オブジェクトとクラスはすべて、人間にとって煙と鏡です。CPU レベルでは、すべてロード メモリ ロケーションとストア メモリ ロケーションです。データは最初に CPU キャッシュに送られます。この場合、f が 1 つのキャッシュ ラインに移動し、fy が別のキャッシュ ラインに移動すると仮定します。ライター スレッドは、最初のキャッシュ ライン (f を保持する) を残りの CPU から見えるようにするために何か他のことを実行します。fy を保持しているものはまだ表示されていません (CPU にそのように指示するものは何もありません)。メモリ位置はまだ 0 です。リーダー スレッドが別の CPU で実行されている場合、別の CPU キャッシュで保留中の変更がこの位置にあることを CPU に通知するものがないため、リーダー スレッドはメモリ位置をロードします。これは、2 番目の CPU がメモリから f と fy をロードすることを意味します。f は最新の値を保持しますが、最新の fy はまだキャッシュ内にあるため、メモリの場所は 0 を保持します。volatile、final などを配置することで、実際にはコンパイラに、CPU にデータを発行するように指示するコードを生成するように指示します。これはほんの一例で、他にもあります。

于 2013-02-07T20:43:14.037 に答える
0

f.y次のように見ることができます0

2つのthreadsT1とT2が実行されているとします。T1はメソッドにアクセスしていますwriterが、T2はreaderの同じオブジェクトのメソッドにアクセスしていますFinalFieldExample

  1. T1はメソッドを呼び出しますwriter()。はfinalとして宣言されているため、すでに3に初期化されています。これにより、コンパイル時定数が一定になり、クラスインスタンスの作成前に発生するf.xクラスの初期化中に指定された値に初期化されます。として宣言されていないFinalFieldExampleため、コンパイラは次の方法でオブジェクトの作成手順のシーケンスを再シャッフルします。 (a)nullではないと宣言されている (b)作成されたオブジェクト(b)そのオブジェクトへの参照。ただし、ポイント(a)がT1によって実行された後、T1はT2によってプリエンプトされますfvolatilefFinalFieldExamplef
  2. T2はメソッドを呼び出しますreader()fnullではないことがわかったため、ifブロック内に入ります。f.xはすでに初期化されて3いるため、i値が割り当てられます3。ただし、上記の手順1でオブジェクトの構築が完了していないため、これf.yまではデフォルト値に初期化されています。0したがってj、値0が割り当てられます。

したがって、変数fを揮発性として 宣言しないと、実行中に呼び出しがインライン化され、共有変数がスレッド間で共有され、ストレージが割り当てられるとすぐに更新されるcompilerように、コードを最適化する自由が得られることがわかりますが、インラインコンストラクターがオブジェクトを初期化する前。constructorfT1T2

于 2013-02-07T20:48:21.117 に答える