内容:通过获取锁的顺序来避免死锁。例如:银行账户转账问题,两个用户转账的话,如果采用一般的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);
}
}
}
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));
else threadPool.execute(orderLock.new MyThread(toAcct, fromAcct, 10));
}
}
}