メモリモデル
Javaメモリモデル
1.原子性、2。可視性、3。順序付け、4。CASおよびアトミッククラス、5。同期最適化
多くの人は[ Javaメモリ構造 ]と[Javaメモリモデル]を区別できません。[Javaメモリモデル]はJavaメモリモデル(JMM)を意味します。
簡単に言うと、JMMは、複数のスレッドが共有データ(メンバー変数、配列)を読み書きするときの一連のルールとデータの可視性、順序、および原子性を保証します。
1.原子性
1.1原子ケース
2つのスレッドが静的変数を初期値0でインクリメントし、もう1つをそれぞれ5000回ずつデクリメントします。結果は0ですか。
1.2問題分析
Javaメモリモデルは次のとおりです。静的変数のインクリメントを完了するには、メインメモリとスレッドメモリでデータを交換する必要があります。
1.3ソリューション
同期(同期キーワード)
構文
synchronized( 对象 ) {
要作为原子操作代码
}
注:オブジェクトの場合、一度に所有者を保持できるスレッドは1つだけです。所有者が空であることが判明した場合、t1スレッドが所有者になり、monitor enterコマンドを使用してモニターがロックされます。t2が来た場合、所有者が見つかります。誰かがEntryListに入り、待機領域のキューに入れられ、ブロックされます。t1が完了すると、監視終了命令は、EntryListに、それが来てグラブを獲得できることを通知します。
2.可視性
最初に現象を見てみましょう。メインスレッドによる実行変数の変更はtスレッドからは見えないため、tスレッドは停止しません。
public class Demo4_2 {
static boolean run = true;
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
while(run){
// ....
// System.out.println(1);
}
});
t.start();
Thread.sleep(1000);
run = false; // 线程t不会如预想的停下来
}
}
2.2ソリューション
volatile(変数キーワード)
メンバー変数および静的メンバー変数を変更するために使用できます。これにより、スレッドがワークキャッシュから変数の値を探すのを回避できます。メインメモリでその値を取得する必要があります。スレッドは、揮発性変数で動作します。メインメモリへの直接アクセス
2.3可視性
同期されたステートメントブロックは、コードブロックの原子性とコードブロック内の変数の可視性の両方を保証できることに注意してください。しかし、欠点は、同期が重い操作であり、パフォーマンスが比較的低いことです。
前の例のデッドループにSystem.out.println()を追加すると、volatile修飾子がなくても、スレッドtが実行変数の変更を正しく認識できることがわかります。
3.注文
3.1奇妙な結果
また
、Java同時ストレステストツールjcstress https://wiki.openjdk.java.net/display/CodeTools/jcstressを使用して、結果が0になる場合もあります。
3.2ソリューション
3.3秩序ある理解
この機能は「命令の再配置」と呼ばれ、マルチスレッドでの**「命令の再配置」は正確さに影響します。たとえば、有名なダブルチェックロックモードはシングルトンを
実装します。上記の実装機能は次のとおりです。
3.4発生前
happen-beforeは、他のスレッドの読み取り操作から見える書き込み操作を指定します。これは、可視性と順序に関する一連の規則です。JMMは、次のrules-before規則を除いて、スレッドが共有変数を書き込むことを保証できません。他のスレッドが共有変数を読み取る
場合は、変数のデフォルト値(0、false、null)が書き込まれます。他のスレッドの場合は、変数が読み取られて
推移的です。xhb-> yおよびy hb-> zの場合、はいx hb-> z
4. CASとアトム
4.1ケース
CASの最下層は、Unsafeクラスに依存して、オペレーティングシステムの最下層のCAS命令を直接呼び出します。次に、スレッドセーフ保護のためにUnsafeオブジェクトを直接使用する例を示します。
4.2楽観的ロックと悲観的ロック
- CASは、楽観的ロックの考え方に基づいています。最も楽観的な見積もりです。他のスレッドが共有変数を変更することを恐れていません。共有変数が変更されても、問題ではありません。もう一度苦労して、もう一度やり直します。
- 同期は悲観的ロックの考え方に基づいています。最も悲観的な見積もりです。他のスレッドが共有変数を変更するのを防ぐ必要があります。ロックするときに変更したくないので、機会がある前にロック解除を理解するだけです。
4.3原子操作
5.同期された最適化
オブジェクトがロックされると、オブジェクトヘッダー情報はマークビット、スレッドロックレコードポインター、ヘビーウェイトロックポインター、スレッドIDなどに置き換えられます。
5.1軽量ロック
各スレッドのスタックフレームには、ロックされたオブジェクトのマークワードを格納できるロックレコード構造が含まれます。
マークワードは8バイトです。オブジェクトがロックされると、オブジェクトヘッダー情報はスレッドのスタックフレームのロックレコードに格納されます。完了後、復元されて置き換えられます。ThreadとMark Wordの間の名刺の交換と同様に、将来的にロックが解除されて置き換えられます。セットに入ると、ロックは成功したことを意味します。失敗した場合は、競争があり、アップグレードする必要があることを意味します。
5.2ロック拡張
軽量ロックを追加しようとしているときにCAS操作が成功しない場合、他のスレッドがこのオブジェクトに軽量ロック(競合)を追加しているため、ロックを拡張する必要があり、ライトが拡張されます。マスロックはヘビーウェイトロックになります。
5.3ウェイトロック
ヘビーウェイトロックが競合する場合は、スピンを使用して最適化することもできます。現在のスレッドが正常にスピンした場合(つまり、ロックを保持しているスレッドがすでに同期ブロックを終了し、この時点でロックを解放した場合)、現在のスレッドはブロックを回避できます。
Java 6以降では、スピンロックはアダプティブです。たとえば、オブジェクトがスピン操作に成功した場合、このスピンが成功する可能性は高いと考えられるため、スピンの回数が増え、そうでない場合はスピンが少なくなります。スピンなし、つまり、よりインテリジェントです。
- スピンはCPU時間を消費し、シングルコアCPUスピンは無駄であり、マルチコアCPUスピンが利用できます。
- これは、赤信号が出て車がオフになるかどうかを確認するようなものです。オフにしないことはスピンに相当し(待機時間は短く費用対効果が高く)、オフにするとブロッキングに相当します(待機時間が長く費用効果が高くなります)
- Java 7の後にスピン機能を有効にするかどうかを制御できません
5.4バイアスロック
5.5その他の最適化
1.ロック時間を短縮し
、コードブロックをできるだけ短くします。
ロック粒度は、2削減
:例えば、ロックの複数に分割されたロックの並行性を増大させる
2.ロックが粗大化
しない多くのサイクルとして、シンクブロック内のシンクブロックに複数のサイクルを
次のようにJVMを最適化することができるまた、複数の追加のロック操作が1回に粗くされます(同じオブジェクトがロックされるため、何度も再入力する必要はありません)。
newStringBuffer().append("a").append("b").append("c");
4.ロックの除去
JVMはコードのエスケープを分析します。たとえば、ロックされたオブジェクトはメソッド内のローカル変数であり、他のスレッドからはアクセスされません。現時点では、すべての同期操作はジャストインタイムコンパイラーによって無視されます。
5.読み取りと書き込みの分離
CopyOnWriteArrayList
ConyOnWriteSet
リファレンス:
https : //wiki.openjdk.java.net/display/HotSpot/Synchronization
http://luojinping.com/2015/07/09/javaロックの最適化/
https://www.infoq.cn/article/java -se-16-synchronized
https://www.jianshu.com/p/9932047a89be
https://www.cnblogs.com/sheeva/p/6366782.html
https:// stackover fl ow.com/questions/46312817/does- java-ever-rebias-an-individual-lock