デッドロックとその原因 - 1

「こんにちは、アミーゴ!」

「今日はデッドロックとは何かについて説明します。」

「ねえ、あなたはすでにそのようなことについて私に話しました。」

「はい、そうしました。しかし今日はこのテーマについてさらに詳しく検討します。」

「最も単純なケースでは、デッドロックには 2 つのスレッドと 2 つのミューテックス オブジェクトが関係します。相互ロックは次の場合に発生します。」

A)各スレッドは両方のミューテックスを取得する必要があります。

B) 最初のスレッドは最初のミューテックスを取得し、2 番目のミューテックスが解放されるのを待っています。

C)  2 番目のスレッドは 2 番目のミューテックスを取得し、最初のミューテックスが解放されるのを待っています。

"ここではいくつかの例を示します:

 public class Student
{
 private ArrayList friends = new ArrayList();

 public synchronized ArrayList getFriends()
 {
  synchronized(friends)
  {
   return new ArrayList(friends);
  }
 }

 public synchronized int getFriendsCount()
 {
  return friends.size();
 }

 public int addFriend(Student student)
 {
  synchronized(friends)
  {
   friends.add(student)
   return getFriendsCount ();
  }
 }
}

「スレッドがgetFriendsメソッドを呼び出し、このオブジェクトのミューテックスを取得し、次にfriendsオブジェクトのミューテックスを取得するとします。」

一方、2 番目のスレッドはaddFriendメソッドを呼び出し、friendsオブジェクトのミューテックスを取得し、次にこのオブジェクトのミューテックスを取得します ( getFriendsCountの呼び出し中に)。

「最初はすべてうまくいきますが、マーフィーの法則にあるように、問題が発生する可能性のあるものはすべて問題になります。最初のスレッドには 1 つのミューテックスしか取得できず、2 番目のスレッドが 2 番目のミューテックスを取得するという状況が必然的に発生します。ミューテックスはその瞬間にハングアップし、他のスレッドがミューテックスを解放するのを永久に待ちます。」

「本で見つけた別の簡単な例を引用することにしました。」

class KnightUtil
{
 public static void kill(Knight knight1, Knight knight2)
 {
  synchronized(knight1)
  {
   synchronized(knight2)
   {
    knight2.live = 0;
    knight1.experience += 100;
   }
  }
 }
}

「2 人の騎士が互いに戦っているゲームがあります。1 人の騎士がもう 1 人の騎士を殺します。この動作はkillメソッドに反映されます。2 つの騎士オブジェクトがそれに渡されます。

「まず、両方のオブジェクトを保護して、他の人が変更できないようにします。」

「二人目の騎士が死亡(HP=0)」

「最初の騎士は 100 XP を獲得します。」

「すべてが順調に見えますが、2 番目のナイトが最初のナイトを同時に攻撃する状況が発生する可能性があります。このメソッドは 2 番目のナイトに対しても呼び出されますが、ナイト オブジェクトは異なる順序で渡されます。」

「デッドロックを引き起こすために複数のメソッドは必要ないということですか?」

「そうです。場合によっては、1 つの単純な方法だけでスレッドとプログラム全体がハングすることがあります。」

「そうですね、この現象は思ったより頻繁に起こるようです。ありがとう、エリー」