2つのアカウント間の送金状況をシミュレートする次のコードがあります
void transfer(Account from,Account to,int money){ from.setAmount(from.getAmount()-money); to.setAmount(to.getAmount()+money); }
シングルスレッドではこのコードは間違いなく問題ありませんが、シングルスレッドでは問題がありますマルチスレッドの下で
ロックします。ロック後のコードは次のとおりです
void transfer(Account from,Account to,int money){ synchronized(from){ synchronized(to){ from.setAmount(from.getAmount()-money); to.setAmount(to.getAmount()+money); } } }
synchronized、つまりオブジェクトのロックです。最初の同期は from オブジェクトをロックし、2 番目の同期は to オブジェクトをロックします。
マルチスレッドの場合、複数のスレッドのうち 1 つのスレッドのみが同時にオブジェクト ロックを取得し、オブジェクト ロック内のコード セグメントを操作できます
しかし、オブジェクト ロックを追加した後、デッドロックが発生する可能性があります。
transfer(a,b,100)とtransfer(b,a,100)は同時に行われます、つまり、aがbに送金するとき、bもaに送金しています
【1】aが送金するbにお金を転送し、スレッドxがそれを取得します aのオブジェクトロックは
【2】bがaにお金を転送し、スレッドyがbのオブジェクトロックを取得します
操作【1】bのオブジェクトロックが緊急に必要です転送操作を実行するには
操作【2】aのオブジェクトが緊急に必要です転送操作はロックをロックすることによってのみ実行できます
両方のスレッドがグループ内の他のスレッドがロックを解放するのを待っており、デッドロックが発生しますこの時に発生します!
どのような点から始めるべきでしょうか?
【1】相互排他待ちを解除する
一般的に、プログラムの安全性のためにオブジェクトをロックする必要があるため、この状態は一般的には解除できません
【2】ホールドを解除して待機する、つまり独占的待機
すべてのリソースを一度に取得できる サンプルコードでは、fromとtoのリソースを一度に取得するのではなく、分散して取得します。
方法 1: to に短いタイムアウトを追加します。 to の取得の試行がタイムアウトしたら、最初に保持されていた from ロックをすぐに解放します
、from と to のロックの取得を再試行します。これが推奨される方法です
方法 2: このコードにグローバル ロックを追加して、from と to が同時に取得されるようにします。転送操作が完了したら、グローバル ロックを解放します。比較的安全ですが、銀行には多くの口座があり、送金操作が非常に頻繁に行われるため、この方法を使用すると、必然的にパフォーマンスが大幅に低下します
[3] 待機中のループを解消します
順番にリソースを取得し、次の手順に従ってください。アカウント ID のサイズ 移管操作では、ID が小さいアカウントが最初に移管操作を実行し、ID が大きいアカウントが後で移管操作を実行します。しかし現実には、IDをもたない、つまり秩序を持たないものもあります。このとき、それらに秩序を強制する必要があります。
【4】取り返しのつかない待ち時間を解消する
ユーザーの転送操作が失敗し、ユーザーエクスペリエンスが良くない場合の最後の手段としてタイムアウトを追加する
個人的に推奨される解決策:
ユーザーの短い待ち時間を犠牲にして、【方法1】を使用します。 2】
関連記事:
Javaにおけるデッドロックの概念と解決策デッドロックの概念とデッドロックの条件以上が【JAVA】デッドロックの発生と解決方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。