Как сделать потокобезопасным основанный на ConcurrentHashMap метод? - PullRequest
0 голосов
/ 04 февраля 2019

В целях обучения параллельности / многопоточности я разрабатываю небольшой API для перевода денег, который будет одновременно вызываться несколькими пользователями.Моя «база данных» - это ConcurrentHashMap<String, Double>, пара ключей / значений которой представляет собой идентификатор учетной записи и ее текущий баланс.

Я знаю, что отдельные операции ConcurrentHashMap (get(), put() и т. Д.)Потоково-безопасный, но метод вывода / депозита будет иметь несколько вызовов методов, что в конечном итоге сделает его не поточно-ориентированным.

Моя проблема: как спроектировать мои методы вывода / депозита, чтобы они были потоко-безопасными?Сначала я думал о том, чтобы сделать их synchronized, но это не имеет никакого смысла, так как я бы отбросил мелкозернистый встроенный механизм синхронизации ConcurrentHashMap.

Это обамои методы снятия и депозита (не беспокойтесь о Double для денег здесь, это не имеет значения в этом контексте):

private void deposit(ConcurrentHashMap<String, Double> myDatabase, String fromAccountId, String toAccountId, double amount) {
    if(myDatabase.get(fromAccountId) < amount) {
        throw new MonetaryOperationViolation("Insufficient funds to perform this operation");
    }

    //Add the amount to the receiver's account
    myDatabase.replace(toAccountId, myDatabase.get(toAccountId), c.get(toAccountId) + amount); //key, oldValue, newValue

    //Withdraw it from the sender's account
    withdraw(myDatabase, fromAccountId, amount);
}

private void withdraw(ConcurrentHashMap<String, Double> myDatabase, String accountId, double amount) {
    if(myDatabase.get(accountId) < amount) {
        throw new MonetaryOperationViolation("Insufficient funds to perform this operation");
    }

    myDatabase.replace(accountId, myDatabase.get(accountId), myDatabase.get(accountId) - amount);
}

Я надеюсь, что я ясно дал понять относительно моей проблемы.Любая помощь будет по достоинству оценена.

Ответы [ 2 ]

0 голосов
/ 06 февраля 2019

Я не думаю, что такую ​​задачу можно решить, просто используя ConcurrentHashMap с атомарным типом.

Представьте себе случай, когда деньги с одного счета были переведены на другой.В этом случае вам нужно синхронизировать не один элемент карты, а две учетные записи одновременно.Это называется транзакцией.Итак, что вам нужно сделать, это реализовать транзакции.Транзакция должна заблокировать все затронутые аккаунты и освободить их после завершения.

В качестве другого варианта вы можете просто создать потокобезопасную очередь с транзакциями и выполнять все транзакции последовательно, и вам не понадобится ни ConcurrentHashMap, ни синхронизация, однако, вероятно, речь идет не о той части, которую вы пытаетесь изучить.

0 голосов
/ 04 февраля 2019

Внутренние компоненты Java имеют много решений для параллелизма, чтобы использовать правильное, вам нужно ответить на простой вопрос: что мое приложение делает большую часть времени?Операции чтения или записи?

В случае, если он выполняет запись (вывод / депозит), я бы рекомендовал использовать java.util.concurrent.atomic.DoubleAdder экземпляр вместо Double, что обеспечит безопасность потока и увеличит пропускную способность вашего приложения в аспекте записи.

В общем, такого рода приложения подходят для модели актеров.Каждый аккаунт может быть представлен актером.Актер будет поддерживать несколько типов сообщений, таких как: снятие / депозит / итого. AKKA framework - отличная реализация актерской модели.

...