JavaEE 09 ロック戦略

1. ロック戦略

1.1 楽観的ロックと悲観的ロック

実際、最初の 3 つのロックは同じ種類のロックですが、異なる観点から説明されています。ここでの楽観主義と悲観主義は、実際には予測の観点からのことを指します。< /span> a>ロック競合の確率を見て、確率が高ければ悲観的ロック、確率が低ければ楽観的ロックです。

楽観的ロックでは、ロック時に実行する処理が少なくなり、ロック速度が速くなりますが、消費される CPU リソースも増加します。悲観的ロックでは、ロックの競合を避けるためにロック時に実行される処理が多くなります。そのため、ロック時に実行される処理が増えます。また、ロックのオーバーヘッドは比較的小さいです。

1.2 軽量ロックと重量ロック

これはロックの規模に基づいており、上記の悲観的ロックと楽観的ロックと本質的に同じです。結果の観点から見ると、軽量ロックオーバーヘッドが大きくなるということです。小さい、重量ロックオーバーヘッドは比較的大きい

1.3 スピン ロックと保留中の待機ロック

スピンロックは軽量ロックと楽観ロックの代表的な実装方法 サスペンドウェイトロックは重量ロックの代表的な実装方法 スピンロックはロックの要否を継続的に判定するwhileループを持っている ロックがかかると飛び出してしまうロックが成功しない場合はブロックせずに判定を続けるため、実質的な処理を行わずにシステムリソースを消費することになり、比較的多くのCPUリソースを消費します。

サスペンドとロックの待機はまったく逆で、サスペンドとロックの待機ではカーネル スケジューラが操作に参加する必要があるため、やるべきことが増えるため、ロックの取得に時間がかかります。

1.4 通常のミューテックス ロックと読み書きロック

通常のミューテックス ロックは同期ロックに似ており、ロックとロック解除の 2 つのアクションがあります。

読み取り/書き込みロックは 2 種類のロックです

読み取りロックと読み取りロックの間でロックの競合は発生しません。

書き込みロックと読み取りロックの間にロックの競合が発生します。

書き込みロック間でもロックの競合が発生します。

概要: 1 つのスレッドが読み取りロックを追加すると、もう 1 つのスレッドは読み取りのみ可能になり、書き込みはできなくなります。

スレッドが書き込みロックを追加すると、他のスレッドは書き込みも読み取りもできなくなります。

これは、MySQL のトランザクションの分離レベルから分離する必要があります。

MySQL のダーティ リード、非反復読み取り、ファントム リード

例として、年齢 = 10 という 1 つのデータを含むテーブルを考えてみましょう。

ダーティ リード: トランザクション A は経過時間 =20 を変更しましたが、トランザクションをコミットしませんでした。トランザクション B がこのデータを読み取った後、トランザクション A はロールバックされました。このとき、B はダーティ データを読み取りました。

非反復読み取り: トランザクション B は age=10 を読み取ります。この時点で、トランザクション A はデータを 20 に変更し、トランザクションを送信します。トランザクション B は再度データをクエリします。見つかった 2 つのデータは異なります。これは非反復読み取りです。 。

ファントム読み取り: トランザクション B が 1 つのデータをクエリします。このとき、トランザクション A は別のデータを追加します。このとき、トランザクション B が再度クエリを実行し、データが 2 つになります。これをファントム リードといいます。

ノンリピータブルリードとダーティリードの違いは次のとおりです。ノンリピータブルリードで読み取られたトランザクションはコミットされています。

1.5 公平なロックと不公平なロック

これは、以前に紹介されたスレッド スターベーションと一定の関係があります。

ここでの公平性とは早い者勝ちを指します。スレッドがロックを解放している限り、最初に待機しているスレッドが最初にロックを取得できます。 . ロックを保持したりロックを解放したりするループ保持状態がなくなり、 他のスレッドが処理できなくなります。

これには、先​​着順機能を実現するためのデータ構造の導入が必要です。

Java のネイティブ ロックは実際には不公平なロックであり、プリエンプティブ実行に依存しています。

1.6 リエントラントロックと非リエントラントロック

以前、Synchronized はリエントラント ロックであると述べましたが、これは、スレッドがこのロックで何度もロックされても、リエントラント ロックはカウンタを増やすだけなので、リエントラント ロックには問題はありません。追加されるロック構造は 1 層のみであり、リエントラントなロックによりデッドロックが発生する可能性があります。

2.同期ロック最適化戦略

Synchronized には適応効果があり、ロックの競合ステータスに基づいてロックの種類を決定できると言われていますが、では正確に何が起こっているのでしょうか? ゆっくり話しましょう。

同期ロックには実際には 3 つの状態があり、状況に応じて段階的に増加します。ここでのロック レベルはダウングレードできないことに注意してください。

1. バイアス ロック状態 (ロックを競合するスレッドがないと仮定)

ここでの中心となるアイデアは、遅延モードのアイデアです。使用するときに作成し、可能であればロックしないでください。いわゆるバイアス ロックは、スレッドに非常に軽量なマークを追加することです。ロックを競合する人がいない場合は、そのようなロック操作を省略し、必要に応じて軽量ロックにアップグレードしてください。

2. 軽量ロック状態(競争力が低いことを想定)

ここでの実装はスピン ロックであり、ロックをすぐに取得できるという利点がありますが、CPU リソースをより多く消費するという欠点があります。

同時に、Synchronized はロックの競合の激しさを計算し、ヘビー級のロックにアップグレードする必要があるかどうかを判断します。

軽量ロックの場合、このロックを競合するスレッドが多数あると仮定すると、現時点ではほとんどのスレッドが回転状態になり、より多くの CPU リソースが消費されます。

3. ヘビー級ロック状態(激しい競技を想定)

このとき、ロックを取得できないスレッドはスピンすることを選択せず​​、直接ブロックして待機し、直接 CPU を放棄し、スレッドが解放されると、ロックを保持するスレッドがランダムに割り当てられます。

4. ロック排除戦略

Syncronized は、スレッドによって追加されたロックが有効かどうかを自動的に判断します。コンパイル中に、無効であると判断された場合は、自動的にロックを削除します。たとえば、複数のスレッドによるメンバー変数の変更などはありません。

5. ロックの粗面化

ロックとロック解除のプロセスが繰り返されることを避けるために、複数のきめの細かいロックが 1 つのきめの粗いロックにマージされます。

3.CAS 戦略 (ロックの使用を回避することでスレッドの安全性の問題を解決する別の戦略)

正式名称はcompare and swap、つまり比較と交換という意味で、実際にはCPU命令です。

CAS に関する API はすべて Java の安全でないパッケージに配置されており、現時点では推奨されていません。

その見方と活用テクニックを簡単に紹介します

これが疑似コードの一部です

アドレス: メモリ内のアドレス

ExpectValue: レジスター 1 の値

swapvalue: レジスター 2 の値

メモリ上の値とレジスタ1の値が同じかどうかを比較し、同じであれば直接交換(代入)します。異なる場合はtrueを返します。 false を返します。

Java 標準ライブラリは、データに対するマルチスレッド操作がアトミックであることを保証するための多くのアトミック クラスも提供します。これは基本的に cas に基づいています。

この時点で、2 つのスレッドを使用して 50,000 回インクリメントすると、スレッドの安全性の問題なく正しい結果が得られます。

オリジナルの標準ライブラリのコードは少し複雑なので、ここでは簡略化して説明します。

ここでの自動インクリメント操作は、最初に古いデータを取得し、次に cas を使用して操作することです。等しいと判断された場合はメモリに直接書き込まれます。等しくない場合は、現在の古い値が自動インクリメントを実行するために、メモリ内の最新の値に更新されます。ここでは、スレッドの安全性の問題は発生しません。

ご質問がございましたら、皆様に訂正していただければと思います。 

おすすめ

転載: blog.csdn.net/qiuqiushuibx/article/details/134906329