1

Java で、2 つの異なるプロセッサ P1 と P2 で同時に実行されている 2 つのスレッド T1 と T2 があるとします。

最初に、スレッド T2objは、(たとえば) 開始メモリ位置 0x1000 に割り当てられたオブジェクトを処理します。これにより、P2 はそのメモリ位置の値を内部的にキャッシュします。次に、T2 はオブジェクトへの (唯一の) 参照を無効にし、ガベージ コレクトされます。

スレッド T1 は次に

    Foo fooRef = new Foo();
    fooRef.x = 10;

fooRef.xこの Foo のインスタンスは、上記の T2 によって解放されたメモリを再利用して割り当てられたためです。

次に、T1 は fooRef 参照をスレッド T2 に渡します (キューまたはその他の共有メモリ メカニズムを介して)。

T2 は、以前にキャッシュされた古い古い値を認識しますか、それとも新しい値 10 を認識しますか?

ハードウェアキャッシュの一貫性メカニズムがないとしましょう。Java 自体は、オブジェクトのメモリの割り当てを解除または割り当てるときに、すべてのプロセッサのキャッシュを確実にクリアしますか? (ハードウェア キャッシュ コヒーレンシ メカニズムが配置されていても、コヒーレンシの伝播は瞬時ではなく、Java 自体による他のコヒーレンシ対策が講じられていない場合、T2 は古い値を読み取る可能性があります)。

4

4 に答える 4

5

適切に同期しない場合、T2は原則として次の3つのうちの1つを見る可能性があります(必ずしも同じ確率であるとは限りません)。

  • (a)明らかに正しく形成されたオブジェクトですが、誤ったデータが含まれています。

  • (b)そもそも適切に形成されていないオブジェクト(つまり、データを気にしないでください。オブジェクトに属する実際のハウスキーピングメタデータが適切に表示されないため、「悪いことが起こる」可能性があります)。

  • (c)誤って「弾丸をかわす」と、T1がオブジェクトを離れたときにT2がオブジェクトを認識します。

適切に同期する(または別の言い方をすれば、オブジェクトを適切に公開する)と、T2はオブジェクトをT1が定義したとおりに認識します。最後のキーワードに関するこの記事と、下部にリンクされているその他の記事では、いくつかの問題と解決策について説明します。オブジェクトパブリッシングとは何ですか、なぜそれが必要なのかという以前の質問に対するこの回答の一部です。役立つかもしれません。

したがって、実質的に[*]常に、適切に同期する必要があります。適切に同期しないと、(a)、(b)、または(c)のいずれの状況が発生するかを推測するのは危険です。

[*]同期が行われることが効果的にわかっている同期ピギーバッキングと呼ばれる手法など、同期の欠如から生じる可能性のあるすべての「パス」を真に計算できれば、同期を安全に回避できる高度な手法が非常にまれにあります。どこかで「時間内に」実行されました。このルートを下りないことをお勧めします!

于 2012-12-13T00:23:36.160 に答える
1

最初のオブジェクトから残った「ジャンク」は表示されません。

オブジェクト内の各プリミティブには、その初期値 ( 、 など) またはある時点でそこに置かれた値のいずれかが含まれ0ますfalse。さらに、プリミティブが 2 語の値 (longまたはdouble) である場合、それらの語のうちの 1 つだけが更新されていることがわかります。このオブジェクトへの書き込みの影響 -- すべての書き込みが表示されていないだけです。しかし、まったく別のランダムなオブジェクトへの書き込みの影響はまだ見られません。

参照値については、初期値 ( null) または構築されたオブジェクトへの正しい参照のいずれかが表示されますが、そのオブジェクトの値は上記と同じあいまいなルールに従います (初期値またはその他の値のいずれかになります)。別のスレッドが挿入され、並べ替えなどが許可されます)。

さて、これが書かれているJLSの正確な場所を実際に見つけることはできません。しかし、それを強く示唆する部分がいくつかあります。たとえば、JLS 17.4.5の例では次のように述べられています。

同期がないため、各読み取りは、初期値の書き込みまたは他のスレッドによる書き込みのいずれかを見ることができます。

私のものを強調しますが、読み取りで確認できる値がリストされていることに注意してください。「各読み取りは、以前のオブジェクトから残ったジャンクバイトを含め、何でも見ることができる」とは言いません。

また、17.4.8 では、別の例で次のように述べられています。

読み取りは各スレッドで最初に行われるため、実行順序の最初のアクションは読み取りでなければなりません。その読み取りが後で発生する書き込みを認識できない場合、読み取る変数の初期値以外の値を認識できません。

(私のものをもう一度強調してください)。これは、「本体」ではなく例ですが、説明したジャンクリードは許可されていないことを明示的に示していることに注意してください。

そして、JLS 17.7はすべて、64 ビット プリミティブの非アトミック性に関するものです (前述のlongand値)。double繰り返しになりますが、表示されるバイト数についてまったく保証がない場合、ある書き込みから 1 つの単語が表示され、別の書き込みから別の単語が表示されることに注意しても意味がありません。言い換えれば、JLS が更新された 1 つの単語のみから生じる「壊れた」値を確認できると述べているという事実は、完全に残ったがらくたから生じる「壊れた」値を確認できないことを強く示唆しています。 .

于 2012-12-13T01:17:42.030 に答える
0

fooRefへのアクセスとへのアクセスfooRef.xが適切に同期されている限り、スレッドT2は の最新の値fooRef.x、つまり 10 を認識します。

于 2012-12-13T00:18:19.597 に答える
0

Java は基盤となるハードウェア キャッシュにアクセスできないため、「すべてのプロセッサのキャッシュを確実にクリアする」わけではありません。

最新の実際の CPU のほとんどは、キャッシュの一貫性を提供します。一部の実際の CPUは、状況によってはメモリ バリアを必要とします。ハードウェア メカニズムのない架空の CPU は、説明されている条件下で古いキャッシュに悩まされる可能性があります。

于 2012-12-13T00:08:53.470 に答える