マルチスレッド シリーズのパート 3 へようこそ!
このパートでは、マルチスレッドにおける デッドロック のメカニズムについて詳しく説明します。その原因、コードが渋滞交差点にならないようにするために使用できる予防戦略とその特定方法。多くの場合、目に見えるエラーが表示されずにアプリケーションが停止してしまい、開発者は困惑し、システムがフリーズしてしまいます。
デッドロックを理解するのに役立つ例えは、交差する線路上に複数の列車がある鉄道網を想像することです。
それぞれの列車が次の列車の発車を待っているため、どの列車も進むことができず行き詰まりが発生します。このシナリオでは、非効率的な信号システムにより、各列車が次のセクションが空いているかどうかを最初に確認することなくそれぞれのセクションに進入することができ、すべての列車が破られないサイクルに閉じ込められました。
この電車の例は、マルチスレッドにおける典型的なデッドロックを示しています。スレッド (電車など) は他のリソースが解放されるのを待っている間、リソース (トラック セクション) を保持しますが、どれも先に進むことができません。ソフトウェアにおけるこの種のデッドロックを防ぐには、循環依存を回避し、各スレッドの安全な通過を確保するために、効果的なリソース管理戦略 (よりスマートな鉄道信号に類似したもの) を実装する必要があります。
デッドロック は、スレッド (またはプロセス) が無期限にブロックされ、他のスレッドが保持するリソースを待機している状況です。このシナリオでは、依存関係の断ち切れないサイクルが発生し、関係するスレッドが先に進むことができなくなります。検出、防止、解決の方法を検討する前に、デッドロックの基本を理解することが不可欠です。
デッドロックが発生するには、コフマン条件として知られる次の 4 つの条件が同時に満たされる必要があります。
相互排他: 少なくとも 1 つのリソースを非共有モードで保持する必要があります。つまり、一度に 1 つのスレッドのみがそのリソースを使用できます。
保留して待機: スレッドは 1 つのリソースを保持し、他のスレッドが保持する追加のリソースを取得するまで待機する必要があります。
プリエンプションなし: スレッドからリソースを強制的に奪うことはできません。彼らは自発的に解放されなければなりません。
循環待機: スレッドの閉じたチェーンが存在し、各スレッドがチェーン内の次のスレッドが必要とするリソースを少なくとも 1 つ保持します。
シーケンス図として理解しよう
上のアニメーションでは、
デッドロックに関する上記の 4 つの条件がすべて存在し、無期限のブロックが発生します。それらのいずれかを破れば、デッドロックを防ぐことができます。
デッドロックの検出は、特に大規模なアプリケーションでは困難な場合があります。ただし、次のアプローチはデッドロックを特定するのに役立ちます
デッドロックのデバッグ/監視方法を理解するための詳細な概要については、「VisualVM と jstack を使用したデッドロックのデバッグと監視」を参照してください
待機ダイおよび創傷待機スキームの適用
Wait-Die スキーム: スレッドが別のスレッドによって保持されているロックを要求すると、データベースは相対的な優先順位を (通常は各スレッドのタイムスタンプに基づいて) 評価します。要求元のスレッドの優先順位が高い場合、スレッドは待機します。それ以外の場合は、停止します (再起動します)。
Wound-Wait スキーム: 要求元のスレッドの優先順位が高い場合、ロックを強制的に解放することで、優先順位の低いスレッドをワインディング (プリエンプト) します。
共有状態の不変オブジェクト
共有状態を可能な限り不変として設計します。不変オブジェクトは変更できないため、同時アクセスにロックが不要となり、デッドロックのリスクが軽減され、コードが簡素化されます。
ロック取得のタイムアウト付き tryLock の使用: 標準の同期ブロックとは異なり、ReentrantLock では、tryLock(timeout,unit) を使用して、指定された期間内にロックの取得を試行できます。ロックがその時間内に取得されない場合、リソースが解放され、無期限のブロックが防止されます。
ReentrantLock lock1 = new ReentrantLock(); ReentrantLock lock2 = new ReentrantLock(); public void acquireLocks() { try { if (lock1.tryLock(100, TimeUnit.MILLISECONDS)) { try { if (lock2.tryLock(100, TimeUnit.MILLISECONDS)) { // Critical section } } finally { lock2.unlock(); } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { lock1.unlock(); } }
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockOrderingExample { private static final Lock lock1 = new ReentrantLock(); private static final Lock lock2 = new ReentrantLock(); public static void main(String[] args) { Thread thread1 = new Thread(() -> { acquireLocksInOrder(lock1, lock2); }); Thread thread2 = new Thread(() -> { acquireLocksInOrder(lock1, lock2); }); thread1.start(); thread2.start(); } private static void acquireLocksInOrder(Lock firstLock, Lock secondLock) { try { firstLock.lock(); System.out.println(Thread.currentThread().getName() + " acquired lock1"); secondLock.lock(); System.out.println(Thread.currentThread().getName() + " acquired lock2"); // Perform some operations } finally { secondLock.unlock(); System.out.println(Thread.currentThread().getName() + " released lock2"); firstLock.unlock(); System.out.println(Thread.currentThread().getName() + " released lock1"); } } }
スレッドセーフ/同時実行コレクションを使用する: Java の java.util.concurrent パッケージは、内部で同期を処理する一般的なデータ構造 (ConcurrentHashMap、CopyOnWriteArrayList など) のスレッドセーフな実装を提供し、明示的なロックの必要性。これらのコレクションは、内部パーティショニングなどの手法を使用して、明示的なロックの必要性を回避するように設計されているため、デッドロックを最小限に抑えます。
ネストされたロックを避ける
循環依存関係を避けるために、同じブロック内での複数のロックの取得を最小限に抑えます。ネストされたロックが必要な場合は、一貫したロック順序を使用してください
初心者であっても、熟練した開発者であっても、同時システムで堅牢で効率的なコードを作成するには、デッドロックを理解することが重要です。この記事では、デッドロックとは何か、その原因、およびデッドロックを防ぐための実践的な方法について説明しました。効果的なリソース割り当て戦略を実装し、タスクの依存関係を分析し、スレッド ダンプやデッドロック検出ツールなどのツールを利用することで、開発者はデッドロックのリスクを最小限に抑え、スムーズな同時実行のためにコードを最適化できます。
マルチスレッドの核となる概念についての旅を続けていきますので、このシリーズの次の記事にご期待ください。 クリティカルセクションについて詳しく説明し、複数のスレッド間で共有リソースを安全に管理する方法を理解します。また、競合状態の概念についても説明します。これは、チェックしないままにすると予測できない動作やバグを引き起こす可能性がある一般的な同時実行の問題です。
各ステップで、アプリケーションをスレッドセーフ、効率的、復元力のあるものにする方法についてより深い洞察が得られます。より優れた、よりパフォーマンスの高いソフトウェアを構築するために、マルチスレッドの知識の限界を押し広げ続けてください!
以上がマルチスレッドの概念パートのデッドロックの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。