小编典典

更好的解决方案,而不是Java中的嵌套同步块?

java

我有一Bank堂课,有一个清单Account。银行有一种transfer()将价值从一个帐户转移到另一个帐户的方法。这个想法是同时锁定转帐中的fromto帐户。

为了解决这个问题,我有以下代码(请记住,这是一个非常琐碎的示例,因为仅仅是一个示例):

public class Account {
    private int mBalance;

    public Account() {
        mBalance = 0;
    }

    public void withdraw(int value) {
        mBalance -= value;
    }

    public void deposit(int value) {
        mBalance += value;
    }
}

public class Bank {
    private List<Account> mAccounts;
    private int mSlots;

    public Bank(int slots) {
        mAccounts = new ArrayList<Account>(Collections.nCopies(slots, new Account()));
        mSlots = slots;
    }

    public void transfer(int fromId, int toId, int value) {
        synchronized(mAccounts.get(fromId, toId)) {
            synchronized(mAccounts.get(toId)) {
                mAccounts.get(fromId).withdraw(value);
                mAccounts.get(toId).deposit(value);
            }
        }
    }
}

这有效,但不能防止 死锁 。要解决此问题,我们需要将同步更改为以下内容:

synchronized(mAccounts.get(Math.min(fromId, toId))) {
    synchronized(mAccounts.get(Math.max(fromId, toId))) {
        mAccounts.get(fromId).withdraw(value);
        mAccounts.get(toId).deposit(value);
    }
}

但是编译器警告我有关 嵌套的同步块
,我相信这是一件坏事吗?另外,我不太喜欢最大/最小解决方案(我不是那个想法的提出者),如果可能的话,我想避免这种情况。

如何解决以上两个问题?如果我们可以锁定多个对象,则可以同时锁定fromto帐户,但据我所知不能这样做。那有什么解决方案?


阅读 228

收藏
2020-11-16

共1个答案

小编典典

锁排序确实是解决方案,所以您是对的。编译器会警告您,因为它无法确保 所有 锁定 均已按
顺序进行—它不够聪明,无法检查您的代码,并且不够聪明,知道可能还有更多的代码。

另一种解决方案是锁定一个封闭的对象,例如,对于一个用户帐户内的转账,您可以锁定用户。用户之间的转移并非如此。

话虽如此,您可能不必依靠Java锁定来进行传输:您需要一些数据存储,通常是数据库。如果使用数据库,锁定将移至存储。仍然适用相同的原则:您订购锁以避免死锁;您可以升级锁以简化锁定。

2020-11-16