Java自身不能防止死锁也不能对死锁进行检测。当两个或多个线程以不同的顺序请求和释放锁时,就可能会发生死锁。所以程序必须以相同的顺序来请求和释放锁,避免产生死锁。
错误示例(不同的锁次序):
final class BankAccount {
private double balanceAmount; // Total amount in bank account
BankAccount(double balance) {
this.balanceAmount = balance;
}
// Deposits the amount from this object instance
// to BankAccount instance argument ba
private void depositAmount(BankAccount ba, double amount) {
synchronized (this) {
synchronized (ba) {
if (amount > balanceAmount) {
throw new IllegalArgumentException("Transfer cannot be completed");
}
ba.balanceAmount += amount;
this.balanceAmount -= amount;
}
}
}
public static void initiateTransfer(final BankAccount first, final BankAccount second, final double amount) {
Thread transfer = new Thread(new Runnable() {
public void run() {
first.depositAmount(second, amount);
}
});
transfer.start();
}
public static void main(String[] args) {
BankAccount bankA = new BankAccount(15000);
BankAccount bankB = new BankAccount(9000);
initiateTransfer(bankA, bankB, 500);
initiateTransfer(bankB, bankA, 1400);
…
}
}
上面的错误示例中会存在死锁的情况。当bankA\bankB两个银行账户同步互相进行转账时,就可能会导致死锁的问题。
正确示例(顺序锁):
final class BankAccount implements Comparable {
private double balanceAmount; // Total amount in bank account
private final Object lock;
private final long id; // Unique for each BankAccount
private static final AtomicLong nextID = new AtomicLong(0); // Next unused
// ID
BankAccount(double balance) {
this.balanceAmount = balance;
this.lock = new Object();
this.id = nextID.getAndIncrement();
}
@Override
public int compareTo(BankAccount ba) {
return (this.id > ba.id) ? 1 : (this.id < ba.id) ? -1 : 0;
}
// Deposits the amount from this object instance
// to BankAccount instance argument ba
public void depositAmount(BankAccount ba, double amount) {
BankAccount former, latter;
if (compareTo(ba) < 0) {
former = this;
latter = ba;
}
else {
former = ba;
latter = this;
}
synchronized (former) {
synchronized (latter) {
if (amount > balanceAmount) {
throw new IllegalArgumentException("Transfer cannot be completed");
}
ba.balanceAmount += amount;
this.balanceAmount -= amount;
}
}
}
public static void initiateTransfer(final BankAccount first, final BankAccount second, final double amount) {
Thread transfer = new Thread(new Runnable() {
@Override
public void run() {
first.depositAmount(second, amount);
}
});
transfer.start();
}
}
上述正确示例可以确保以相同的顺序获得和释放多个锁。上述代码中BankAccount对象有一致的次序,BankAccount对象实现了Java.lang.Comparable接口,并覆写了compareTo方法。当发生转账时,会对两个BankAccount对象进行排序,会按排序结果顺序锁定两个BankAccount对象。当两个线程向两个账户之间互相转账时,这两个线程会按相同的次序对账户进行锁定,这样可以保证只有一个线程完成转账后,另一个线程才能获获取到所有的锁,然后进行转账操作。