ホームページ  >  記事  >  Java  >  JAVA 仮想マシン (JVM) の詳細な紹介 (8) - 効率的な同時実行

JAVA 仮想マシン (JVM) の詳細な紹介 (8) - 効率的な同時実行

王林
王林転載
2019-08-24 17:25:482366ブラウズ

JAVA 仮想マシン (JVM) の詳細な紹介 (8) - 効率的な同時実行

#メモリ モデル

メモリ モデルは、特定のメモリまたはキャッシュに対する特定の操作プロトコルです。読み取りおよび書き込みアクセスのプロセスを抽象化します。その主な目的は、プログラム内のさまざまな変数のアクセス ルールを定義することです。

#メインメモリとワーキングメモリ

JAVA 仮想マシン (JVM) の詳細な紹介 (8) - 効率的な同時実行

すべての変数はメイン メモリに保存されます。各スレッドにも独自の作業メモリがあります。作業メモリには、スレッドが使用する変数のメイン メモリのコピーがあります。スレッドのコピーは、変数 読み込みや代入などの操作はワーキングメモリ上で行う必要があり、メインメモリ上の変数を直接読み込むことはできません。

メモリ間対話操作

メイン メモリから作業メモリにコピー: 読み取り操作とロード操作を順番に実行します。

ワーキングメモリはメインメモリに同期されます: 保存および書き込み操作。


volatile の特徴

Volatile は synchronized と同じ機能を持ちますが、synchronized よりも軽量です。その主な機能は次のとおりです。

すべてのスレッドに対してこの変数の可視性を確保します。

これは何を意味しますか?これは、スレッドがこの変数の値を変更すると、新しい値が他のスレッドに即座に認識されることを意味します。通常の変数ではこれができません。スレッド間の通常の変数の値の転送は、メイン メモリを介して完了する必要があります。たとえば、スレッド A は通常の変数の値を変更し、それをメイン メモリに書き戻します。別のスレッドB はスレッド B にあります。A がライトバックを完了し、メイン メモリから読み取ると、新しい変数値がスレッド B に表示されるようになります。

命令の並べ替えの最適化を無効にします

命令の並べ替えはプログラムの同時実行を妨げるためです。

マルチスレッド

理由マルチスレッドが必要ですか? コンピュータの計算速度とストレージおよび通信サブシステムの速度との差が大きすぎるため、ディスク I/O、ネットワーク通信、および通信に多くの時間が費やされています。データベースへのアクセス。マルチスレッドを使用すると、CPU をより効率的に利用できます。

#同時アプリケーションのシナリオとは何ですか?

コンピュータのプロセッサを最大限に活用する

サーバーは同時に複数のクライアントにサービスを提供します

プロセッサを内蔵する方法コンピューティングユニットは十分に活用されていますか?

#キャッシュ層の追加

操作に必要なデータをキャッシュにコピーして、操作を実行できるようにします。早くやれよ。操作が完了すると、キャッシュからメモリーに同期して戻されるため、プロセッサーは遅いメモリーの読み取りおよび書き込みを待つ必要がなくなります。ただし、キャッシュの一貫性をどのように確保するかという考慮すべき問題があります。

JAVA 仮想マシン (JVM) の詳細な紹介 (8) - 効率的な同時実行

#入力コードのアウトオブオーダー実行の最適化

#スレッド実装

カーネル スレッドを使用した実装

カーネル スレッドは、オペレーティング システム カーネルによって直接サポートされるスレッドです。

ユーザー スレッドを使用して達成する

ユーザー スレッドの確立、同期、破棄、スケジューリングは、カーネルの助けを借りずにユーザー モードで完全に完了します。カーネルはスレッドの存在を認識しない実装です。この実装はほとんど使用されません。

#ユーザー スレッドと軽量スレッドを使用してハイブリッド実装を行う

マージ

スレッド スケジューリング

## スレッド スケジューリングシステムがプロセッサの使用権をスレッドに割り当てるプロセスを指します。協調型とプリエンプティブ型の 2 つの主なタイプがあります。

協調的

スレッドの実行時間はスレッド自体によって制御されます。スレッドが作業の実行を終了すると、別のスレッドに切り替えるようにシステムに能動的に通知します。糸。

利点は、実装が簡単で、スレッド同期の問題がないことです。欠点は、スレッドのプログラミングに問題があり、システムがスレッドを切り替えるように指示されていない場合、プログラムは常にそこでブロックされ、システムが簡単にクラッシュする可能性があることです。

プリエンプティブ


スレッドにはシステムによって実行時間が割り当てられ、スレッドの切り替えはそれ自体によって決定されません。これは、Java で使用されるスレッド スケジューリング方法です。
#スレッド セーフティ

#複数のスレッドがオブジェクトにアクセスする場合、ランタイム環境でのこのスレッドのスケジューリングが考慮されない場合代替実行の場合、追加の同期や呼び出し元でのその他の調整操作は必要ありません。このオブジェクトを呼び出す動作によって正しい結果が得られる場合、このオブジェクトは安全です。

#共有データの分類

不変

不変共有データは、final で変更されたデータであり、スレッドセーフである必要があります。共有データが基本型変数の場合は、それを定義するときに Final キーワードを使用するだけです。


共有データがオブジェクトの場合、オブジェクトの動作がその状態に影響を与える必要はありません。オブジェクト内の状態を持つすべての変数を Final として宣言できます。たとえば、String クラスは不変クラスです。

完全にスレッドセーフです。

自分自身を Java API のスレッドセーフ クラスとしてマークします。ほとんどのクラスは完全にスレッドセーフではありません。たとえば、Vector はスレッドセーフなコレクションであり、そのすべてのメソッドは同期されるように変更されていますが、マルチスレッド環境では依然として同期されません。

相対スレッド セーフティ

相対スレッド セーフティは、通常スレッド セーフティと呼ばれるものです。このオブジェクトに対する個々の操作がスレッドセーフであることを保証します。ただし、連続呼び出しの一部の特定のシーケンスでは、呼び出しの正確性を保証するために呼び出し側で追加の同期手段を使用する必要がある場合があります。
ほとんどのスレッドセーフなクラスはこのタイプに属します。

スレッド互換性

オブジェクト自体は線形的に安全ではありませんが、正しく使用することで作成できます。呼び出し側の同期手段 オブジェクトが同時環境で安全に使用できるようにするための手段。スレッドセーフではないほとんどのクラスがこのカテゴリに分類されます。

スレッドの対立

いかなる理由があっても、次のようなマルチスレッド環境で同時に使用することはできません。 System.setIn()、System.SetOut()。 1 つは入力を変更し、もう 1 つは出力を変更します。この 2 つを「交互に」実行することはできません。

#実装方法

##方法 1: 相互排他的な同期 - 悲観的な同時実行戦略

(1) synchronized

原則: コンパイル後、このキーワードは synchronized ブロック内に置かれます。 2 つのバイトコード命令Monitorenter と Monitorexit はそれぞれ前後に形成されます。 Monitorenter 命令が実行されると、プログラムはオブジェクトのロックを取得しようとします。取得できた場合、ロック カウンタは 1 に設定されます。同様に、monitorexit が実行されると、ロック カウンタは -1 になります。カウンタが 0 になるとロックが解除されます。

その特徴は、同じスレッドに対して再入可能であること、入力されたスレッドが実行を完了する前に、同期ブロックが後続の他のスレッドの入力をブロックすることです。

選択シナリオは次のとおりです。これは重量が大きいため、絶対に必要な場合にのみ使用してください。

(2) ReentrantLock

このリエントラント ロックは、java.util.concurrent (JUC) パッケージの下のクラスです。その高度な機能には、待機の中断、公平なロックの実装、およびロックを複数の条件にバインドできることが含まれます。

方法 2: ノンブロッキング同期 - オプティミスティック同時実行戦略

競合する他のスレッドがない場合は、最初に操作を実行します。共有データの場合、操作は成功します。共有データの競合が発生して競合が発生した場合は、他の補償措置が取られます。

#方法 3: 同期スキームなし

この方法ではデータを共有しないため、同期対策は必要ありません。反復可能なコードやスレッドローカルストレージなど。

(1) リエントラントコード

メソッドの戻り結果が予測可能で、同じデータが入力される限り同じ結果を返すことができる場合、そのメソッドはリエントラント要件を満たしています。

(2) スレッド ローカル ストレージ

コードの一部で必要なデータを他のコードと共有する必要があり、データを共有するコードが同じスレッドで実行される場合は、次のようになります。共有データの表示範囲は 1 つのスレッドに制限されているため、スレッド間でデータ競合の問題が発生しないようにするための同期は必要ありません。

ロックの最適化

アダプティブ スピン

JAVA スレッドのブロックまたはウェイクアップのためオペレーティング システムは CPU の状態を切り替える必要があり、この状態の移行にはプロセッサ時間がかかります。同期コードブロックの内容が単純すぎる場合、状態遷移にユーザーコードの実行時間よりも時間がかかる可能性があります。

この問題を解決するには、ロックを要求している後続のスレッドを「しばらく待機」させ、ビジー ループを実行してスピンさせます。現時点では、プロセッサの実行時間は放棄されません。スピンが制限回数を超えてもロックが正常に取得されない場合は、従来の方法を使用してスレッドが一時停止されます。

では、アダプティブスピンとは何でしょうか?

同じロック オブジェクト上で、スピン待機がロックの取得に成功したばかりの場合、仮想マシンはスピンによってロックを取得できる確率が非常に高いと判断し、スピンを許可します。 . 待ち時間は比較的長くなります。逆に、スピンによりオーバーロックの取得に成功することがほとんどない場合には、スピン工程を省略してもよい。

#ロックの削除

は、ジャストイン仮想マシンを指します。コンパイラの実行時に、一部のコードで同期が必要だが、共有データの競合が発生する可能性が低いと検出されるロックを削除します。

ロック粗大化

一連の連続した操作で同じオブジェクトのロックとロック解除が繰り返される場合、またはループ本体内でロック操作が発生する場合、スレッドの競合がない場合でも、相互排他同期操作が頻繁に行われるため、不必要なパフォーマンスの低下が発生します。 。
一連の断片化された操作がすべて同じオブジェクトをロックしていることを仮想マシンが検出すると、ロック同期の範囲が操作シーケンス全体の外側に粗くなり、ロックが必要なのは 1 回だけになります。

軽量ロック

マルチスレッドの競合がなければ、パフォーマンスを低下させる従来の重いロックオペレーティング システムのミューテックスを使用したペナルティ。
適用可能なシナリオ: 実際の競合はなく、複数のスレッドがロックを交互に使用します。短期間のロック競合は許可されます。

バイアス ロック

バイアス ロックは、競合のないロックの使用を減らすために使用されます。 1 つのスレッドのみ ロックの場合、軽量ロックの使用によって生じるパフォーマンスの消費。軽量ロックでは、ロックの適用と解放のたびに少なくとも 1 つの CAS が必要ですが、バイアスされたロックでは初期化中に 1 つの CAS のみが必要です。
適用可能なシナリオ: 実際の競合はなく、ロックを申請した最初のスレッドのみが今後そのロックを使用します。

上記は、JAVA 仮想マシンにおける効率的な同時実行の詳細な紹介です。その他の関連する質問については、PHP 中国語 Web サイトを参照してください: JAVA ビデオ チュートリアル

以上がJAVA 仮想マシン (JVM) の詳細な紹介 (8) - 効率的な同時実行の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcsdn.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。