避免多个对象事务死锁的最佳方法?

Best way to avoid deadlock for several objects transaction?

我在寻找死锁示例时偶然发现了这段代码:

package com.example.thread.deadlock._synchronized;

public class BankAccount {
    double balance;
    int id;

    BankAccount(int id, double balance) {
        this.id = id;
        this.balance = balance;
    }

    void withdraw(double amount) {
        // Wait to simulate io like database access ...
        try {Thread.sleep(10l);} catch (InterruptedException e) {}
        balance -= amount;
    }

    void deposit(double amount) {
        // Wait to simulate io like database access ...
        try {Thread.sleep(10l);} catch (InterruptedException e) {}
        balance += amount;
    }

    static void transfer(BankAccount from, BankAccount to, double amount) {
        synchronized(from) {
            from.withdraw(amount);
            synchronized(to) {
                to.deposit(amount);
            }
        }
    }

    public static void main(String[] args) {
        final BankAccount fooAccount = new BankAccount(1, 100d);
        final BankAccount barAccount = new BankAccount(2, 100d);

        new Thread() {
            public void run() {
                BankAccount.transfer(fooAccount, barAccount, 10d);
            }
        }.start();

        new Thread() {
            public void run() {
                BankAccount.transfer(barAccount, fooAccount, 10d);
            }
        }.start();

    }
}

您将如何更改 transfer 方法以使其不会导致死锁?首先想到的是为所有帐户创建一个共享锁,但这当然会杀死所有并发。那么有没有什么好的方法可以只锁定涉及交易的两个账户而不影响其他账户呢?

单独使用两个同步块而不是嵌套。

synchronized(from){
    from.withdraw(amount);
}
synchronized(to){
    to.deposit(amount);
}

所以在 from.withdraw(amount) 被调用之后, from 上的锁在尝试锁定 to

之前被释放

在多锁情况下避免死锁的一种方法是始终以相同的顺序锁定对象。

在这种情况下,这意味着您将为所有 BankAccount 个对象创建总排序。幸运的是我们有一个可以使用的 id,所以你总是可以先锁定较低的 id,然后(在另一个同步块内)较高的 id。

这假设没有 BankAccount 个具有相同 ID 的对象,但这似乎是一个合理的假设。