サービスのパフォーマンスを測定する場合、1 秒あたりのトランザクション処理 (TPS) は重要な指標の 1 つであり、TPS 値はプログラムの同時実行能力と非常に密接な関係があります。
以下は読書メモです。前回の記事でも紹介しました: Java JMM: メモリモデル
1. ハードウェアの効率と一貫性
- メモリモデル: 特定の動作プロトコル下での特定のメモリまたはキャッシュへの読み取りおよび書き込みアクセスのためのプロセスの抽象化
- 演算ユニットを最大限に活用するために、プロセッサは入力コードのアウトオブオーダー実行を最適化し、計算後にアウトオブオーダー実行の結果を再編成します。
2. Java メモリ モデル
- Java メモリ モデルの主な目的は、プログラム内のさまざまな変数のアクセス ルールを定義することです。Java マルチスレッド メモリ モデルは、CPU キャッシュ モデルに似ています。CPU キャッシュ モデルに基づいて確立されます。Java スレッド メモリモデルは標準化されており、基盤となるさまざまなコンピューター間の差異が保護されます。
- メインメモリとワーキングメモリ
- Java メモリ モデルでは、すべての変数がメイン メモリに格納され、各スレッドにも独自の作業メモリがあることが規定されています。
- 異なるスレッドは互いの作業メモリ内の変数に直接アクセスできず、スレッド間の変数値の転送はメインメモリを通じて完了する必要があります。
- メモリ間の相互作用
- Java 仮想マシンは、次の操作に対してアトミックであり、分割不可能である必要があります。
- ロック (lock): 変数をスレッド専用の状態として識別し、メインメモリに作用します。
- ロック解除(ロック解除): ロック状態にある変数を解放し、メインメモリに作用します。
- read (読み取り): 変数の値をメインメモリからスレッドの作業メモリに転送し、メインメモリ上で動作します。
- ロード(load): リード操作でメインメモリから取得した変数値をワーキングメモリの変数コピーに入れ、ワーキングメモリに対して作用します。
- use (使用): 作業メモリ内の変数の値を実行エンジンに渡し、作業メモリに作用します。
- Assign(代入):実行エンジンから受け取った値をワーキングメモリ上の変数に代入し、ワーキングメモリに作用します。
- ストア(ストレージ):作業メモリ内の変数の値をメインメモリに転送し、作業メモリに作用します。
- write(ライト):作業メモリからストア操作で得られた変数値をメインメモリの変数に代入し、メインメモリに対して作用します。
- メイン メモリを作業メモリにコピーし、読み取り操作とロード操作を順番に実行し、作業メモリからメイン メモリに同期して戻し、ストア操作と書き込み操作を順番に実行します。
- Java 仮想マシンは、次の操作に対してアトミックであり、分割不可能である必要があります。
- 揮発性変数の特別なルール
- Volatile は、Java 仮想マシンが提供する最も軽量な同期メカニズムであると言えます。
- volatile の 2 つの特性を定義する
- すべてのスレッドに対してこの変数の可視性を保証します
- 可視性: スレッドが変数の値を変更すると、他のスレッドはすぐにそれを知ることができます。
- 揮発性変数に基づく操作は、同時実行下ではスレッドセーフです
- 原子性を確保するにはロックが依然として必要です
- 検出された操作の結果は、変数の現在の値に依存しないか、単一のスレッドのみが変数の値を変更することを保証できます。
変数は、他の状態変数との不変制約に参加する必要はありません。
- 検出された操作の結果は、変数の現在の値に依存しないか、単一のスレッドのみが変数の値を変更することを保証できます。
- 命令の並べ替えの最適化を無効にする
- ハードウェア アーキテクチャの観点からは、プログラムで指定されたシーケンスなしで、複数の命令を対応する各回路ユニットに個別に送信して処理することができます。
- すべてのスレッドに対してこの変数の可視性を保証します
- 揮発性変数の読み取り操作のパフォーマンス消費は、通常の変数の場合とほぼ同じです。
- long 変数と double 変数の特別な規則
- 仮想マシンが、volatile によって変更されていない 64 ビット データ型のロード、ストア、読み取り、書き込みの 4 つの操作のアトミック性を実行できるようにします。これは、「long と double」の非アトミックな合意です。
- double 型の場合、現代の中央処理装置は一般に、単精度および倍精度浮動小数点データの処理に特化した特殊な浮動小数点ユニット FPU を備えているため、非アトミック アクセスの問題は発生しません。
- 原子性、可視性、および順序
- 原子性
- Java メモリ モデルによって直接保証されるアトミック変数操作には、読み取り、ロード、割り当て、使用、保存、書き込みが含まれます。
- 基本的なデータ型のアクセス、読み取り、書き込みはアトミックであると考えることができます。
- 大規模なアトミック性を保証するには、ロックとロック解除を使用して操作する必要があります
- 同期されたブロック間の操作もアトミックです
- Java メモリ モデルによって直接保証されるアトミック変数操作には、読み取り、ロード、割り当て、使用、保存、書き込みが含まれます。
- 可視性
- スレッドが変数を変更すると、他のスレッドはそれをすぐに知ることができます
- 揮発性の特別ルールにより、新しい値が直ちにメイン メモリに同期され、使用される直前にメイン メモリからリフレッシュされることが保証されます。
- 同期と最終的な可視性も実現可能
- 同期ブロックが変数のロック解除を操作する前に、変数をメイン メモリに同期して戻す必要があります。
- 最終的な初期化が完了し、コンストラクターが "this" 参照を渡さない場合、最終フィールドの値は他のスレッドで確認できるようになります。
- 秩序
- スレッド内で観察すると、すべての操作が順序どおりである一方、あるスレッドでは、別のスレッドのすべての操作が順序どおりではありません。
- スレッド間での揮発性の同期保持順序
- volatile には命令の並べ替えを禁止するセマンティクスがあります
- synchronized 変数は 1 つのスレッドのみが同時にロックできるようにします
- ほとんどの同時実行制御操作は、同期を使用して実行できます。
- 原子性
- 先行原理
- Java メモリ モデルにおけるすべての順序付けは、揮発性および同期化されたメモリによってのみ実現されます。
- Happen-before は、Java メモリ モデルで定義された 2 つの操作間の部分的な順序関係です。
- A は B の前に起こり、A によってもたらされた効果は B によって観察されます。
- プログラム順序規則、モニターロック規則、揮発性変数規則、スレッド開始規則、スレッド終了規則、スレッド中断規則、オブジェクト終了規則、推移性
- 基本的に時系列と発生先行原則には因果関係がないため、同時多発的に発生するセキュリティ問題を計測する際には、時間の順序に惑わされることなく、全て発生先主義に基づいて計測が行われます。
3. Java とスレッド
- スレッドの実装
- スレッドはプロセスよりも軽量なスケジューリング実行単位であり、Java のプロセッサ リソース スケジューリングの最も基本的な単位です。
- Thread クラスのすべての主要なメソッドはネイティブです
- ネイティブ メソッドとは、多くの場合、このメソッドがプラットフォームに依存しない手段を使用しない、または実装できないことを意味します。
- カーネル スレッド (KLT) 実装: オペレーティング システム カーネルによって直接サポートされるスレッド、1:1 実装
- 通常、プログラムはカーネル スレッドを直接使用しませんが、カーネル スレッドの高レベル インターフェイスであるライトウェイト プロセス (LWP) を使用します。
- 軽量プロセスの制限
- カーネルスレッドの実装に基づいて、さまざまなスレッド操作にはシステムコールが必要です
- ユーザースレッド実装:1:N実装
- 広義には、カーネルスレッドでない限り、ユーザースレッドの一種と考えることができます。
- 狭義: 完全にユーザー空間のスレッド ライブラリ上に構築される
- ハイブリッド実装:N:M実装
- カーネルスレッドはユーザースレッドと一緒に使用されます
- Javaスレッドの実装
- HotSpot: 各 Java スレッドは、オペレーティング システムのネイティブ スレッドに直接マップされ、次のことを実現します。
- Javaスレッドのスケジューリング
- 協調的なスレッドのスケジューリング
- スレッドの実行時間はスレッド自体によって制御され、別のスレッドに切り替えるにはシステムに能動的に通知する必要があります。
- 利点: 実装が簡単
- 短所: スレッドの実行時間は制御できず、1 つのスレッドが原因でプロセス全体またはシステムさえもブロックされます (1 つのスレッドに問題がある場合、システムはスレッドを切り替えるように通知されません)。
- プリエンプティブなスレッドのスケジューリング
- 各スレッドの実行時間はシステムによって割り当てられ、スレッドの実行時間はシステムによって制御されるため、1 つのスレッドがシステム全体をブロックするような問題は発生しません。
- Java で使用されるスレッド スケジューリング方法はプリエンプティブ スケジューリングです。
- 協調的なスレッドのスケジューリング
- 状態遷移
- 新規(New): 作成後に起動されていないスレッド
- 実行中 (実行可能): 実行中であるか、オペレーティング システムが実行時間を割り当てるのを待っている可能性があります。
- 無期限に待機: 他のスレッドが明示的にウェイクアップするのを待機します。
- 時間指定待機: 一定時間が経過するとシステムによって自動的に起動します。
- Blocked: スレッドはブロックされており、排他ロックの取得を待機しています。このイベントは、別のスレッドがロックを放棄し、プログラムが同期領域に入るのを待機しているときに発生します。
- 終了: 終了したスレッドのスレッド状態