Сохранение порядка блокировки во вложенных синхронизированных блоках

1

У меня есть пример, похожий на тот, что в этом вопросе.

После того, как я гарантирую сохранение порядка полученных замков

public class Account {
    private int balance;

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

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

public class Bank {
    private List<Account> accounts;
    private int slots;

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

    public void transfer(int fromId, int toId, int value) {
        synchronized(accounts.get(Math.min(fromId, toId))) {
            synchronized(accounts.get(Math.max(fromId, toId))) {
                accounts.get(fromId).withdraw(value);
                accounts.get(toId).deposit(value);
        }
    }
}

Я понимаю, что если один поток выполняет передачу из учетной записи A в B, а другой поток хочет сделать передачу из B в A, тогда он должен ждать блокировки (исправьте меня, если я ошибаюсь).

Каков наилучший способ гарантировать, что только один поток может сделать передачу от A до B, но другой поток может переходить от B к A?

Я думал об объявлении двух наборов блокировок для каждого объекта Account:

public class Account{
    public Object fromLock = new Object();
    public Object toLock = new Object();
    // ...
}

И получить один из них в зависимости от того, переводится ли она с данной учетной записи или с нее. Это правильный путь?

РЕДАКТИРОВАТЬ: Вопрос: предположим, что методы снятия и сдачи требуют времени. Как бы вы разрешили двум пользователям (связанным с разными потоками) переводить друг друга в одно и то же время, не дожидаясь ожидания?

  • 0
    Быстрый комментарий, ваш конструктор вы установите mBalance = 0 это не нужно в Java. Вы можете прочитать docs.oracle.com/javase/tutorial/java/nutsandbolts/… для получения дополнительной информации. Также не часто имена ваших переменных называются «m» en.wikipedia.org/wiki/Naming_convention_(programming)#Java
  • 0
    Это не имеет большого смысла: ваша учетная запись не будет более поточно-ориентированной, если один поток удаляет, а второй - депозит. Состояние mBalance должно охраняться одним замком.
Показать ещё 4 комментария
Теги:
multithreading
locking
synchronized

1 ответ

1

Как вы внедрили Учетную запись, вы никогда не захотите, чтобы несколько транзакций влияли на одну учетную запись в любой момент времени. В частности, поскольку "баланс" - это int, а "вывести" и "депозит" напрямую изменить значение "баланса" напрямую, используя неатомные операции, вы никогда не захотите, чтобы вызывы "вывести" и "депозит" чередовали,

С другой стороны, если бы вы представляли "баланс" как AtomicInteger, тогда призывы "снять" и "депозит" можно было бы смело перемешать. Однако, если вы использовали как "fromLock", так и "toLock", то максимум одна учетная запись могла одновременно переводить деньги на любую учетную запись. Например, предположим, что учетные записи B и C оба хотят перевести деньги на счет A - используя "toLock", эти две передачи должны быть сериализованы.

Однако, отступив немного, почему необходимо предотвратить одновременные депозиты в данную учетную запись (при условии, что баланс счета мутируется поточно-безопасным образом)? Конечно, необходимо обеспечить, чтобы любая конкретная учетная запись могла быть задействована не более одной транзакции в качестве учетной записи "из" в любой момент времени; но я не понимаю, почему такое ограничение должно существовать для учетной записи "to" (опять же, считая, что "баланс" управляется поточно-безопасным образом).

Итак, если я не пропущу что-то здесь, я думаю, что представлять "баланс" как AtomicInteger и только захват блокировки учетной записи "from" внутри "transfer" должен удовлетворять вашим требованиям.

ПРИМЕЧАНИЕ. Прежде чем я вернусь в забвение, позвольте мне сказать, что мои комментарии применимы только к конкретному способу представления банковских счетов в вашем образце кода (т.е. В производственных системах, которые делают такие вещи, есть веские причины для обеспечения того, чтобы парные депозиты и снятие средств осуществляются в рамках транзакций).

Ещё вопросы

Сообщество Overcoder
Наверх
Меню