Javaメモリ モデルは、Java プログラミング言語のスレッドがメモリを介してどのように相互作用するかを説明します。メモリ モデルは、コードのシングル スレッド実行の説明とともに、Java プログラミング言語の セマンティクスを提供します。
1995年に開発されたオリジナルのJavaメモリモデルは、広く壊れていると認識されており[1]、多くの実行時最適化を妨げ、コードの安全性を十分に保証していませんでした。これは、 Java Community Processを通じてJava仕様要求133(JSR-133)として更新され、2004年にTiger(Java 5.0)で発効しました。[2] [3]
コンテクスト
Javaプログラミング言語とプラットフォームは、スレッド機能を提供します。スレッド間の同期は、開発者にとって非常に難しいことで知られています。Java アプリケーションは、さまざまなプロセッサとオペレーティング システムで実行できるため、この難しさはさらに増します。プログラムの動作について結論を導き出すには、Java の設計者は、すべての Java プログラムの可能な動作を明確に定義する必要があると判断しました。
現代のプラットフォームでは、コードは記述された順序どおりに実行されないことがよくあります。最大のパフォーマンスを実現するために、コンパイラ、プロセッサ、メモリ サブシステムによって順序が変更されます。マルチプロセッサアーキテクチャでは、個々のプロセッサがメイン メモリと同期していない独自のローカル キャッシュを持つ場合があります。スレッドが互いに完全に同期していることを要求することは、パフォーマンスの観点からコストがかかりすぎるため、一般的に望ましくありません。つまり、特定の時点で、異なるスレッドが同じ共有データに対して異なる値を参照する可能性があります。
シングルスレッド環境では、コード実行について推論するのは簡単です。一般的なアプローチでは、システムが個々のスレッドを個別にシリアルとして扱うセマンティクスを実装する必要があります。個々のスレッドが実行されると、そのスレッドによって実行されるすべてのアクションは、アクション自体が順序どおりに実行されなくても、プログラムに記述されている順序どおりに実行されるように見えます。
あるスレッドが命令を順不同で実行した場合、たとえそれが最初のスレッドのセマンティクスに影響を与えなかったとしても、別のスレッドはそれらの命令が順不同で実行されたという事実を認識する可能性があります。たとえば、変数 x と y が両方とも 0 に初期化され、次の命令が同時に実行される 2 つのスレッドを考えてみましょう。
並べ替えが実行されず、スレッド 2 の y の読み取りが値 2 を返す場合、x への書き込みが y への書き込みの前に実行されたため、後続の x の読み取りは値 1 を返す必要があります。ただし、2 つの書き込みが並べ替えられると、y の読み取りは値 2 を返し、x の読み取りは値 0 を返す可能性があります。
Java メモリ モデル (JMM) は、マルチスレッド プログラムの許容される動作を定義し、このような順序変更が可能な場合について説明します。一貫性と信頼性のある Java アプリケーションを実現するために、スレッドとメイン メモリの関係に実行時間の制約を設定します。これにより、動的コンパイラ、プロセッサ、およびキャッシュによって実行される最適化に対しても、マルチスレッド環境でのコード実行を推論できるようになります。
記憶モデル
単一スレッドの実行の場合、ルールは単純です。Java言語仕様では、Java 仮想マシンがスレッド内でシリアルのようなセマンティクスを遵守することを要求しています。ランタイム (この場合、通常は動的コンパイラ、プロセッサ、メモリ サブシステムを指します) は、スレッドの分離の結果が、すべてのステートメントがプログラム内で発生した順序 (プログラム順序とも呼ばれます) で実行された場合とまったく同じになることが保証されている限り、任意の有用な実行最適化を自由に導入できます。[4]
これの主な注意点は、as-if-serialセマンティクスでは、異なるスレッドがデータの異なるビューを持つことを妨げないということです。メモリ モデルは、データが読み取られたときにどの値を返すことができるかについて明確なガイダンスを提供します。基本ルールは、スレッドのas-if-serialセマンティクスに違反しない限り、個々のアクションを並べ替えることができること、およびロックの取得や解放などのスレッド間の通信を意味するアクションでは 、それより前に発生するアクションが、その効果を確認する他のスレッドによって確認できることを意味しています。たとえば、ロックの解放前に発生するすべてのことは、同じロックのその後の取得後に発生するすべてのものよりも順序付けられ、見えるようになります。[5]
数学的には、プログラムによって実行されるすべてのアクションには、事前発生順序と呼ばれる部分的な順序があります。事前発生順序はプログラム順序を包含します。プログラム順序で 1 つのアクションが別のアクションの前に発生する場合、事前発生順序でもそのアクションは別のアクションの前に発生します。さらに、ロックの解放とその後の取得は、事前発生グラフのエッジを形成します。読み取りは、その書き込みが事前発生順序の何らかのパスに沿った読み取り前のその変数への最後の書き込みである場合、または書き込みが事前発生順序でその読み取りに対して順序付けられていない場合、書き込みの値を返すことができます。
インパクト
Javaメモリモデルは、一般的なプログラミング言語に包括的なメモリモデルを提供する最初の試みでした。[6] これは、並行システムや並列システムの普及が進み、そのようなシステムに対して明確なセマンティクスを備えたツールや技術を提供する必要性が高まったために正当化されました。それ以来、メモリモデルの必要性はより広く受け入れられ、C++などの言語にも同様のセマンティクスが提供されています。[7]
参照
参考文献
- ^ Pugh, William (2000). 「Java メモリモデルには致命的な欠陥がある」(PDF) .並行性: 実践と経験. 12 (6): 445– 455. doi :10.1002/1096-9128(200005)12:6<445::AID-CPE484>3.0.CO;2-A . 2021 年7 月 15 日閲覧。
- ^ Goetz, Brian (2004-02-24). 「Java メモリ モデルの修正、パート 2」(PDF) . IBM . 2010-10-18に閲覧。
- ^ Jeremy Manson および Brian Goetz (2004 年 2 月)。「JSR 133 (Java メモリ モデル) FAQ」。2010 年 10 月 18 日取得。Java
メモリ モデルは、マルチスレッド コードで有効な動作と、スレッドがメモリを介してやり取りする方法を説明します。プログラム内の変数の関係と、実際のコンピュータ システムでメモリまたはレジスタに変数を保存したり、そこから変数を取得したりするための低レベルの詳細を説明します。これは、さまざまなハードウェアとさまざまなコンパイラ最適化を使用して正しく実装できる方法で行われます。
- ^ マンソン、ジェレミー。「JSR-133 FAQ」。
- ^ 「第 17 章 スレッドとロック」。docs.oracle.com。
- ^ Goetz, Brian (2004-02-24). 「Java メモリ モデルの修正、パート 1」(PDF) . IBM . 2008-02-17に取得。
- ^ Boehm, Hans. 「C++ のスレッドとメモリ モデル」 。2014年 8 月 8 日閲覧。
外部リンク
- Java の理論と実践: Java メモリ モデルの修正、パート 1 - オリジナルの Java メモリ モデルの問題について説明する記事。
- Java の理論と実践: Java メモリ モデルの修正、パート 2 - JSR 133 が Java メモリ モデルに加えた変更について説明します。
- Java メモリ モデル プラグマティクス (トランスクリプト)
- Javaメモリモデルリンク
- Javaの内部構造
- JSR-133 ウェブページ
- JSR-133 よくある質問
- JSR-133 実装ガイド
