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

Leo*_*Bor 0 java concurrency multithreading synchronized

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

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();

    }
}
Run Code Online (Sandbox Code Playgroud)

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

Joa*_*uer 5

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

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

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