例子
银行账户转账问题,两个用户转账的话,如果采用一般的synchronized嵌套的话,容易造成死锁,现在我们通过类似哲学家问题的解决方案一样:先获取同一个锁,才有资格获取下一个。而判断是通过System.identityHashCode()来生成类的hashcode()的返回值作为唯一标识,相同的话,我们再加一把锁。
// 死锁版本
class Account {
private int money;
public Account(int money) {
this.money = money;
}
public void debit(int amount) {
System.out.println("after debit " + amount + " " + this.money + " -> " + (this.money-amount));
this.money -= amount;
}
public void credit(int amount) {
System.out.println("after credit " + amount + " " + this.money + " -> " + (this.money+amount));
this.money += amount;
}
public int get() {
return this.money;
}
}
public class OrderLock {
private static final Object tieLock = new Object();
public void transferMoney(final Account fromAcct, final Account toAcct, final int amount)
throws InsufficientResourcesException {
class Helper {
public void transfer() throws InsufficientResourcesException {
if (fromAcct.get() < amount)
throw new InsufficientResourcesException();
else {
fromAcct.debit(amount);
toAcct.credit(amount);
}
}
}
// 两个用户使用这两个账户给对方转账时,死锁;因为一方fromAcct账户为对方的toAcct账户
synchronized (fromAcct) {
synchronized (toAcct) {
new Helper().transfer();
}
}
}
class MyThread implements Runnable {
private Account fromAcct;
private Account toAcct;
private int amount;
public MyThread(Account fromAcct, Account toAcct, int amount) {
this.fromAcct = fromAcct;
this.toAcct = toAcct;
this.amount = amount;
}
@Override
public void run() {
try {
transferMoney(this.fromAcct, this.toAcct, this.amount);
} catch (InsufficientResourcesException e) {
System.out.println("操作失败");
}
}
}
public static void main(String[] args) {
Account fromAcct = new Account(100);
Account toAcct = new Account(230);
OrderLock orderLock = new OrderLock();
ExecutorService threadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
if ((i & 1) == 0)
threadPool.execute(orderLock.new MyThread(fromAcct, toAcct, 10));
else threadPool.execute(orderLock.new MyThread(toAcct, fromAcct, 10));
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
// 解决死锁版本
class Account {
private int money;
public Account(int money) {
this.money = money;
}
public void debit(int amount) {
System.out.println("after debit " + amount + " " + this.money + " -> " + (this.money-amount));
this.money -= amount;
}
public void credit(int amount) {
System.out.println("after credit " + amount + " " + this.money + " -> " + (this.money+amount));
this.money += amount;
}
public int get() {
return this.money;
}
}
public class OrderLock {
private static final Object tieLock = new Object();
public void transferMoney(final Account fromAcct, final Account toAcct, final int amount)
throws InsufficientResourcesException {
class Helper {
public void transfer() throws InsufficientResourcesException {
if (fromAcct.get() < amount)
throw new InsufficientResourcesException();
else {
fromAcct.debit(amount);
toAcct.credit(amount);
}
}
}
// 转账双方共用这两个账户的对象,否则无法通过下面方式排序下面的锁顺序
int fromHash = System.identityHashCode(fromAcct);
int toHash = System.identityHashCode(toAcct);
if (fromHash < toHash) {
synchronized (fromAcct) {
synchronized (toAcct) {
new Helper().transfer();
}
}
} else if (fromHash > toHash) {
synchronized (toAcct) {
synchronized (fromAcct) {
new Helper().transfer();
}
}
} else {
synchronized (tieLock) {
synchronized (fromAcct) {
synchronized (toAcct) {
new Helper().transfer();
}
}
}
}
}
class MyThread implements Runnable {
private Account fromAcct;
private Account toAcct;
private int amount;
public MyThread(Account fromAcct, Account toAcct, int amount) {
this.fromAcct = fromAcct;
this.toAcct = toAcct;
this.amount = amount;
}
@Override
public void run() {
try {
transferMoney(this.fromAcct, this.toAcct, this.amount);
} catch (InsufficientResourcesException e) {
System.out.println("操作失败");
}
}
}
public static void main(String[] args) {
// 转账双方共用这两个账户对象
Account fromAcct = new Account(100);
Account toAcct = new Account(230);
OrderLock orderLock = new OrderLock();
ExecutorService threadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
if ((i & 1) == 0)
threadPool.execute(orderLock.new MyThread(fromAcct, toAcct, 10));
// 注:转账的账户变成了toAcct,被转账的账户变成了fromAcct
else threadPool.execute(orderLock.new MyThread(toAcct, fromAcct, 10));
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
关于上面代码的疑惑
1 嵌套synchronized 代码块
synchronized (fromAcct) {
synchronized (toAcct) {
new Helper().transfer();
}
}
- 1
- 2
- 3
- 4
- 5
理解:嵌套的synchronized是按照嵌套的顺序获取锁,即先获取最外层的锁fromAcct,再获取toAcct的锁。
2 fromAcct和toAcct的锁
注:转账双发的账户放生了反转,Jack的toAcct变成了Bob的fromAcct
死锁的四个必要条件
互斥使用(资源独占)
一个资源每次只能给一个进程使用不可抢占(不可剥夺)
资源申请者不能强行的从资源占有者手中夺取资源,资源 只能由占有者自愿释放请求和保持(部分分配,占有申请)
一个进程在申请新的资源的同时保持对原有资源的占有(只要这样才是动态申请,动态分配)循环等待
存在一个进程等待队列{P1, P2, … ,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,星辰给一个进程等待环路
hashCode和identityHashCode
hashCode()方法时Object类下面的一个方法,供继承类重写,根据对象内存地址计算哈希值,String类重写了hashCode方法,并改为根据字符序列计算哈希值。
identityHashCode()方法是System类中的静态方法,根据对象内存地址来计算哈希值
public static void main(String[] args) {
// TODO Auto-generated method stub
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println("s1的hashCode值::" + s1.hashCode());
System.out.println("s2的hashCode值::" + s2.hashCode());
System.out.println("s1的identityHashCode值::" + System.identityHashCode(s1));
System.out.println("s2的identityHashCode值::" + System.identityHashCode(s2));
System.out.println("--------------------------");
String s3 = "Java";
String s4 = "Java";
System.out.println("s3的hashCode值::" + s3.hashCode());
System.out.println("s4的hashCode值::" + s4.hashCode());
System.out.println("s3的identityHashCode值::" + System.identityHashCode(s3));
System.out.println("s4的identityHashCode值::" + System.identityHashCode(s4));
}
// print
s1的hashCode值::99162322
s2的hashCode值::99162322
s1的identityHashCode值::817899724
s2的identityHashCode值::397836821
--------------------------
s3的hashCode值::2301506
s4的hashCode值::2301506
s3的identityHashCode值::1326857436
s4的identityHashCode值::1326857436