基本概念
1. 可視性
あるスレッドが共有変数を変更すると、別のスレッドは変更された値を読み取ることができます。
2. メモリバリア
プロセッサがメモリ操作に順次制限を実装するための一連の命令。
3. バッファライン
CPU はキャッシュに割り当てることができるストレージの最小単位を指示し、プロセッサがキャッシュ ラインを埋めると、キャッシュ ライン全体がロードされます。
4.ロックプレフィックス付き命令
ロックプレフィックス付き命令は、マルチコアプロセッサ上で 2 つのことを行います:
1) 現在のプロセッサのキャッシュラインのデータをシステムメモリに関連付けます。
2) メモリに書き戻すこの操作により、他の CPU によってそのメモリ アドレスにキャッシュされたデータが無効になります。
5. キャッシュ一貫性プロトコル
マルチプロセッサでは、各プロセッサのキャッシュの値が一貫していることを、バス上に広がるデータをスニッフィングすることによって確認します。プロセッサは、そのキャッシュ ラインに対応するアドレスが変更されたことを検出すると、現在のプロセッサのキャッシュ ラインを無効な状態に設定します。プロセッサがこのデータを読み書きするとき、メモリからプロセッサ キャッシュにデータを再読み込みします。
6.CAS
CompareAndSwap 比較と交換
CAS 操作では、古い値 (CAS 操作を実行する前の値、期待値) と新しい値の 2 つの値を入力する必要があります。これは、現在の値の場合にのみ実行できます。古い値と等しい 現在の値を新しい値に設定します。それ以外の場合は設定しません。これはアトミックな操作であり、ハードウェアによって保証されます。
7. 並べ替えルール
基本的に、プログラムの実行結果が変更されない限り (シングルスレッド環境または正しく同期されたマルチスレッド環境を指します)、JMM にはコンパイラとプロセッサに対する並べ替え制限が 1 つだけあります。プロセッサーがそれを最適化できます。
Volatile
上記のロックプレフィックス命令とキャッシュ一貫性プロトコルからわかるように、これが volatile の実装原理です。
実際、valatile 変数が書き込まれるとき、可視性の目的を達成するために Lock プレフィックスが実際に追加されます。
final
Final フィールドは明示的に 1 回しか割り当てることができませんが、これは、final フィールドを複数回初期化できないという意味ではありません。
例:final int i; i は、コンストラクターで割り当てられる前にデフォルト値 0 に初期化されます。これはコードをデバッグすることで証明できます。
初期化前に最終フィールドの値がアクセスされないようにするために、プログラマは 1 つのことだけを確認する必要があります。それは、コンストラクター内で、構築されるオブジェクト (this) が「エスケープ」しないことです。同期手段がなければ、基本型や参照型を含む任意のスレッドで認識される最終フィールドがコンストラクターを通じて正しく初期化されていることを保証できます。
エスケープされるオブジェクトの例:
public class FinalTest{ final int i; static FinalTest obj; public FinalTest(){ i =1; /** *这里会使正在被构造的对象逸出,如果和上一句做了重排序,那么其他线程就可以通过obj访问到还为被初始化的final域。 **/ obj = this; } }
Happens-Before ルール
happens-before の意味
Happen-Before ルールは、2 つの操作間の順序関係を記述するために使用されます。スレッドが 1 つであるかどうか。この順序は厳密には実行時の順序を意味するものではなく、前の操作の結果が後続の操作から見えることを意味します。
Happens-Before 関係は次のように定義されます:
1 つの操作が別の操作の前に発生する場合、最初の操作の実行結果は 2 番目の操作に表示され、最初の操作の実行順序は 2 番目の操作の後にランク付けされます。 2 つの操作間に前発生関係が存在することは、Java プラットフォームの特定の実装が前発生関係で指定された順序で実行される必要があることを意味するものではありません。並べ替え後の実行結果が、前発生関係に従った実行結果と一致する場合、この並べ替えは不正ではありません。
たとえば、プログラムの実行順序で A が B より前にあり、A が共有変数を変更し、B が偶然その共有変数を使用する場合、A は B より前に発生する必要があります。より率直に言うと、A は次のことを行う必要があります。 share 変数の変更は、B の実行時に B に表示される必要があります。
happens-before ルール
プログラム シーケンス ルール: スレッド内のすべての操作は、そのスレッド内の後続の操作よりも前に発生します。
ロック ルールの監視: ロックのロック解除は、その後のロックのロックの前に行われます。
volatile ルール: volatile フィールドへの書き込みは、この volatile フィールドの後続の読み取りよりも前に行われます。
推移性: A が B より前に発生し、B が C より前に発生する場合、A は C より前に発生します。
start() ルール: スレッド A が操作 ThreadB.start() を実行する場合、スレッド A の ThreadB.start() 操作はスレッド B の操作よりも先に発生します。
join() ルール: スレッド A が ThreadB.join() 操作を実行して正常に戻った場合、スレッド B の操作はすべて、スレッド A が ThreadB.join() 操作から正常に戻る前に発生します。
これらすべてのルールの説明: Aappens-before B は、A が B より前に起こらなければならないという意味ではありませんが、A が B より前に起こった場合、A の演算結果が B に見える必要があることを意味します