死锁及其原因 - 1

“嗨,阿米戈!”

“今天我要告诉你什么是死锁。”

“嘿,你已经和我说过类似的事情了。”

“是的,我做到了。但今天我们将更详细地考虑这个话题。”

“在最简单的情况下,死锁涉及两个线程和两个互斥对象。互锁发生在:”

A)每个线程都需要获取两个互斥量。

B) 第一个线程已经获取了第一个互斥锁,正在等待第二个互斥锁被释放。

C) 第二个线程已经获得了第二个互斥锁,正在等待第一个互斥锁被释放。

“这里有些例子:

例子
 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方法,获取this对象的互斥量,然后获取friends对象的互斥量。”

“与此同时,第二个线程调用addFriend方法,获取好友对象的互斥量,然后获取此对象的互斥量(在调用getFriendsCount期间)。”

“起初,一切都会好起来的,但正如墨菲定律所说:任何可能出错的事情都会出错。不可避免地会出现第一个线程只有时间获取一个互斥锁,而第二个线程将获取第二个互斥锁的情况就在那一刻互斥锁。它们会像那样挂起,永远等待另一个线程释放互斥锁。”

“我决定引用我在书中找到的另一个简单例子:”

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

”有一个游戏,两个骑士在打架。一个骑士杀死了另一个。这种行为体现在 kill方法中。将两个骑士对象传递给它。

“首先,我们保护这两个对象,这样其他人就无法更改它们。”

“第二个骑士死了(HP = 0)”

“第一个骑士获得 100 XP。”

“一切似乎都很好,但可能会出现第二个骑士同时攻击第一个骑士的情况。第二个骑士也调用了这种方法,但骑士对象的传递顺序不同。”

“你的意思是我们甚至不需要多种方法来陷入僵局?”

“是的。有时只需要一种简单的方法就会导致线程和整个程序挂起。”

“是啊,我想这种现象比我想象的更频繁发生。谢谢,艾莉。”