java锁 两个顺序的同步引起的问题
java中存在对多个对象加锁的情况,例如: 在银行系统转账时,需要锁定两个账户,这个时候,顺序使用两个synchronized可能存在死锁的情况,在网上搜索到下面的例子:
Java代码
1.public class Bank {
2. final static Object obj_lock = new Object();
3.
4. // Deadlock crisis
5. public void transferMoney(Account from, Account to, int number) {
6. synchronized (from) {
7. synchronized (to) {
8. from.debit();
9. to.credit();
10. }
11. }
12. }
13.
14. // Thread safe
15. public void transferMoney2(final Account from, final Account to, int number) {
16. class Help {
17. void transferMoney2() {
18. from.debit();
19. to.credit();
20. }
21. }
22.
23. int fromHash = from.hashCode();
24. int toHash = to.hashCode();
25. if (fromHash < toHash) {
26. synchronized (from) {
27. synchronized (to) {
28. new Help().transferMoney2();
29. }
30. }
31. } else if (toHash < fromHash) {
32. synchronized (to) {
33. synchronized (from) {
34. new Help().transferMoney2();
35. }
36. }
37. } else {
38. synchronized (obj_lock) {
39. synchronized (to) {
40. synchronized (from) {
41. new Help().transferMoney2();
42. }
43. }
44. }
45. }
46. }
47.}
public class Bank {
final static Object obj_lock = new Object();
// Deadlock crisis
public void transferMoney(Account from, Account to, int number) {
synchronized (from) {
synchronized (to) {
from.debit();
to.credit();
}
}
}
// Thread safe
public void transferMoney2(final Account from, final Account to, int number) {
class Help {
void transferMoney2() {
from.debit();
to.credit();
}
}
int fromHash = from.hashCode();
int toHash = to.hashCode();
if (fromHash < toHash) {
synchronized (from) {
synchronized (to) {
new Help().transferMoney2();
}
}
} else if (toHash < fromHash) {
synchronized (to) {
synchronized (from) {
new Help().transferMoney2();
}
}
} else {
synchronized (obj_lock) {
synchronized (to) {
synchronized (from) {
new Help().transferMoney2();
}
}
}
}
}
}
若操作账户A,B:
1 A的hashCode小于B, 先锁A再锁B
2 B的hashCode小于A, 先锁B再锁A
3 产生的hashCode相等,先锁住一个全局静态变量,在锁A,B
这样就避免了两个线程分别操作账户A,B和B,A而产生死锁的情况。
注意点:
1 需要为Account对象写一个好的hashCode算法,使得不同账户间产生的hashCode尽量不同。
2 如果某次产生hashCode相同锁住obj_lock,那么如果有新的操作再次产生相同hashCode,就必须等待前一次操作完成,可能产生性能问题。(这里应该有更好的方法)