[[436819]]序文Synchronized ロックと ReentrantLock ロックは、並行プログラミングでよく使用されます。分散ロックはビジネス開発プロセスでも使用できます。分散ロックによく使用されるフレームワークは、Redis をベースにした分散ロック フレームワークである Redisson と、Zookeeper をベースにした分散ロック フレームワークである Curator です。もちろん、ロックを実装する方法は他にもありますが、ここでは紹介しません。 この記事は主に、Java ロックと分散ロックのソースコードを研究した後の要約です。 1ロックの最も基本的な要素なぜロックを使用するのですか? ロックが必要な理由については、答えは明白です。「マルチスレッドの同時実行の競合を回避するため」です。 複数のスレッドでパブリック データを変更する場合は、1 つのスレッドだけが操作を実行していることを確認する必要があります。ここでのパブリック データは、パブリック変数またはデータベース内のデータ行になります。 ロックの基本要素 ロックが必要な理由がわかったら、ロックの基本要素を導き出すことができます。 - ロックサイン: 正常にロックするには何が必要ですか?
- ロックホルダー: どのスレッドがロックを保持していますか?
- ロック再入: 自分自身をロックした後、再度ロックしますか?
- ロックの同時実行: スレッドが同時にロックできない場合はどうすればよいですか?
- ロック解除:使用後にロックを解除するにはどうすればいいですか?
簡単に言えば、これらが要素です。抜けている部分があれば追加していただいて構いません。 2ロック記号ロック フラグには、ロックが成功したかどうかを示すフラグが必要であり、このロック フラグはアトミック性を保証する必要があります。 - 基礎となる同期メソッドは C++ で実装されています。 ObjectMonitor オブジェクトには、ロックが保持されているかどうかを識別するために使用される _count パラメータがあります。
- ReentrantLock は AQS に基づいて実装されており、状態はスレッドがロックを保持しているかどうかを示します。 ReentrantReadWriteLock も AQS に基づいて実装されており、状態の上位 16 ビットは読み取りロックを表し、下位 16 ビットは書き込みロックを表します。
- Redisson は Redis のハッシュ データ構造に基づいて実装されており、ロック キーの存在によってロックされているかどうかが示されます。
- Curator は ZooKeeper の一時的なシーケンシャル ノードに基づいて実装されます。ロック パスの下にノードが存在するかどうかを判断して、ロックするかどうかを示します。
ロックホルダー3個- ロックの保持者は間違いなく現在のスレッドですが、分散ロックでは、サービス間のスレッドの競合を防ぐためのマシンも必要です。
- synchronized ObjectMonitor オブジェクトでは、_owner はロックを取得したスレッドを参照します。
- ReentrantLock と ReentrantReadWriteLock は AQS Node ノード内の Thread オブジェクトであり、ロックを取得するスレッドを表すために使用されます。
- Redisson は、複数のサービス インスタンスがある場合に同時実行の競合を防ぐために、ハッシュ データ構造の field フィールドに UUID:ThreadId を保存します。
- Curator によって作成された一時的なシーケンシャル ノードには、/locks/lock_01/_c_UUID-lock-0000000000 のような構造を持つ、ロックされたマシンを示す UUID が含まれます。
4 ロックの再入力ロックを取得したスレッドが再度ロックを取得しようとすると、カウントが保証されます。 - 同期すると、_count が蓄積され、CAS で更新されます。
- ReentrantLock は AQS の状態を蓄積し、CAS を更新します。
- Redisson は Lua スクリプトを使用してハッシュ構造の値を蓄積します。
- Curator は、再入を示すために Java コード内に AtomicInteger 型の lockCount フィールドを保持します。
5ロック待機- 同期された同時ロックが失敗した場合、スレッドはスピンして待機し、バイアス ロック、軽量ロック、重量ロックのロック アップグレード プロセスが実行されます。
- 当初はロック解除されていた
- バイアス ロック: 同期されたコードのセクションは常に 1 つのスレッドによってアクセスされ、自動的にロックが取得されます。ほとんどの場合、ロックは同じスレッドによって取得されるため、偏ったロックが発生します。目的は、同期されたコード ブロックを 1 つのスレッドのみが実行する場合のパフォーマンスを向上させることです。 JDK 6以降のJVMではデフォルトで有効になっています。オブジェクトヘッダーフラグ(01-ロック解除)バイアスロックフラグ(1-バイアスロック)かどうか
- 軽量ロック: ロックがバイアス ロックの場合、他のスレッドによってアクセスされ、バイアス ロックが軽量ロックにアップグレードされます。他のスレッドは、スピンしてロック オブジェクト ヘッダー フラグ 00 を取得しようとします。
- 重量ロック: 待機中のスレッドは 1 つだけであり、スレッドはロックを取得するために待機しています。一定数のスピンが発生するか、1 つのスレッドがロックを保持し、他のスレッドがスピンしているときに 3 番目のスレッドが来ると、ロックは重量級のロックにアップグレードされます。オブジェクト ヘッダー フラグ ビット 10
- ReentrantLock は、前のノードが仮想ヘッド ノードであるかどうかを監視するために AQS 双方向同期キューに配置されます。そうであれば、ロックを取得しようとします。
- 不公平なロックの場合、新しいスレッドはデフォルトでロックの取得に参加しますが、公平なロックの場合は、最初にキューが空かどうかを確認します。
- LockSupport.park() メソッドはスピン待機中に使用され、CPU リソースを放棄し、他のスレッドは LockSupport.unpark() を呼び出します。
- tryLock メソッドを使用して時間を設定できます。ロック取得に失敗した場合、または指定時間内に中断された場合は、ロック取得失敗が返されます。
- Redisson の同時ロックでは、失敗したスレッドは現在のロックのタイムアウトを取得し、その後、Semaphore の tryAcquire メソッドを通じて一定時間ブロックした後、再度ロックの取得を試みます。
- 公平ロックは、さらに、スレッド待機キューとして Redis のリスト データ構造を使用し、待機中のスレッドの順序を格納するためにソート セットの順序付きセット データ構造を使用します (スコアはタイムアウトのタイムスタンプです)。
- Curator 同時ロックは、一時的なシーケンシャル ノードを直接作成し、シーケンシャル ノード内の前のノードを監視します (群集効果を防ぐため)。デフォルトはフェアロックです。
6 ロック解除- 同期を手動で解除する必要はありません。
- ReentrantLock は状態を 0 に減分した後、後続のノードを起動し、後続のノードの一部は LockSupport.unpark(s.thread) を呼び出す必要があります。
- Redisson はキーを積極的に解放し、直接削除します。タイムアウト解放は、サービスがダウンしており、リースを更新するウォッチドッグが存在しないことを意味します。指定された時間が経過すると、Redis キーは自動的に解放されます。
- キュレーターは積極的にノードを解放し、削除します。サービスが停止した場合、ノードは自動的に削除されます。
7 結論この記事では、ロックと分散ロックの基本要素を複数の観点から要約し、分析します。 MySQL などのデータベースに基づくロックも実装の参考として使用できます。 この記事はWeChatの公開アカウント「Programmer Xiaohang」から転載したもので、以下のQRコードからフォローできます。この記事を転載する場合は、プログラマーXiaohangの公式アカウントまでご連絡ください。 |