web-dev-qa-db-ja.com

スレッドキャッシングとJavaメモリモデル

Javaメモリモデルとスレッドを理解しようとしています。fasが理解しているように、各スレッドには「メイン」メモリのローカルコピーがあります。したがって、1つのスレッドがint変数は、たとえば、いくつかのオブジェクトの場合、int変数をキャッシュし、それを変更すると、他のスレッドは変更を認識しない場合があります。この場合、スレッドはそれをキャッシュしますか?スレッドがオブジェクトへの参照をキャッシュする場合、オブジェクトの状態への変更は他のスレッドには見えません?なぜですか?

前もって感謝します

26
user1409534

CPUには、異なるレベルのキャッシュL1、L2、L3があります。すべてのCPU(および/ may CPUコア)には独自のキャッシュがあります。このキャッシュには、パフォーマンスのためにメインメモリ(RAM)の最小セットが格納されます。

  _______________    ______________  
 |     CPU 1     |  |     CPU 2    |  
 |   _________   |  |   _________  |  
 |  | Level 1 |  |  |  | Level 1 | |  
 |  |   Cache |  |  |  |  Cache  | |  
 |  |         |  |  |  |         | |
 |  |_________|  |  |  |_________| |  
 |_______________|  |______________|
           | |              | |
           | |              | |
          _|_|______________|_|__
         |                       |
         |      MAIN MEMORY      | 
         |_______________________|


  Time     Command                 CPU 1 (Cache)      CPU 2 (Cache)        Main Memory     
-------  ----------              ----------------    --------------       -------------
  1          ---                       ---                ---                x = 10
  2       Read x  (on cpu1)           x = 10              ---                x = 10
  3       Write x <--20 (on cpu1)     x = 20              ---                x = 10       
  4       Read  x (on cpu2)           x = 20              x = 10             x = 10
  5       put cache to Main mem       x = 20              x = 10             x = 20

たとえば、上記の実行順序では、xの値がCPU2で間違っています。 x値はCPU1によってすでに変更されています。 x変数が揮発性として定義されている場合、すべての書き込み操作は即座にメインメモリに反映されます。

25

スレッドにはメモリのローカルコピーがありません。スレッドが読み書きするメモリの一部は、メインメモリではなく、キャッシュからのものである可能性があります。キャッシュは互いに同期している必要はなく、メインメモリと同期している必要もありません。したがって、ここで不整合を観察できます。

したがって、あるオブジェクトのint変数を変更しようとすると、そのスレッドはint変数をキャッシュし、それを変更すると、他のスレッドは変更を認識できない場合があります。

それは正しいです。 Javaメモリモデルは、ルールの前に発生するように定義されています。たとえば、フィールドxの揮発性書き込みとフィールドxの揮発性読み取りの間に発生前ルールがあります。したがって、書き込みが完了すると、その後の読み取りでは、書き込まれた値が表示されます。

そのような発生前の関係がなければ、すべての賭けはオフになります(ルールの発生前に発生しない場合、命令の順序変更も人生を複雑にする可能性があります)。

スレッドがオブジェクトへの参照をキャッシュする場合、オブジェクトの状態への変更は他のスレッドにも表示されませんか?どうして?

見えるかもしれない…見えないかもしれないルールが起こらない限り、すべての賭けは有効です。さもなければ、スピードアップのためのハードウェアトリックやコンパイラトリックのような多くの最適化が許可されないためです。そしてもちろん、メモリを常にキャッシュと同期させると、パフォーマンスが低下します。

14
pveentjer

CPUには複数のキャッシュがあります。データのコピーに一貫性がない可能性があるのは、これらのハードウェアキャッシュです。一貫性がない可能性がある理由は、すべてを一貫性のある状態に保つと、コードが10倍遅くなり、複数のスレッドを使用することで得られる利点が損なわれる可能性があるためです。まともなパフォーマンスを得るには、選択的に一貫している必要があります。 Javaメモリモデルは、データの整合性を保証するタイミングを記述しますが、最も単純なケースではそうではありません。

注:これは単なるCPUの問題ではありません。スレッド間で一貫している必要がないフィールドは、コードにインライン化できます。つまり、あるスレッドが値を変更した場合、コードに焼き付けられているため、別のスレッドがこの変更を確認することはありません。

13
Peter Lawrey

「しかし、きちんとしたマルチスレッドコードを書く前に、マルチスレッドコードの複雑さと微妙さについてさらに研究する必要があります。

スレッドに関しては、ほとんど保証されていません。

2つの異なるスレッドがクラスの単一のインスタンスにアクセスし、両方のスレッドがそのオブジェクトのメソッドを呼び出し、それらのメソッドがオブジェクトの状態を変更するときに発生する大混乱を想像できますか? ...視覚化するのも怖いです。 "、Sun Certified Programmer for Java 6、Chapter 9:Threads。から

私の友人、

Javaでは、スレッドはオブジェクトや変数をキャッシュせず、(参照があるインスタンスオブジェクトにスレッドキャッシュメモリについて話すことは、オペレーティングシステムスレッドについて話すことに似ています... Javaスレッドが内部でどのように管理されているかに関係なく、すべてのOSで同じように動作します。これは、OSによって大きく異なります。

このコードを見てください:

AccountDanger r = new AccountDanger();
Thread one = new Thread(r):
Thread two = new Thread(r);

ご覧のとおり、この場合、スレッドは同じインスタンスrにアクセスできます。次に、同期の問題が発生します、確かに... ネイティブまたはオブジェクトのメンバーについて話しても問題ありません。スレッド1と2はすべてにアクセスできますrのメンバー(スコープまたはセッター/ゲッターを介してアクセス可能である場合)rから直接値を読み取りますインスタンス。気づかなくても、これは確かに難しいことです。

マルチスレッドアプリケーションをコーディングする場合は、JavaスコープおよびJava同期について読むことをお勧めします。

よろしく、

2
Rodmar Conde