“嗨,阿米戈!”
“今天我要告诉你什么是死锁。”
“嘿,你已经和我说过类似的事情了。”
“是的,我做到了。但今天我们将更详细地考虑这个话题。”
“在最简单的情况下,死锁涉及两个线程和两个互斥对象。互锁发生在:”
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。”
“一切似乎都很好,但可能会出现第二个骑士同时攻击第一个骑士的情况。第二个骑士也调用了这种方法,但骑士对象的传递顺序不同。”
“你的意思是我们甚至不需要多种方法来陷入僵局?”
“是的。有时只需要一种简单的方法就会导致线程和整个程序挂起。”
“是啊,我想这种现象比我想象的更频繁发生。谢谢,艾莉。”
GO TO FULL VERSION