如何使基于ConcurrentHashMap的方法线程安全?

时间:2019-02-04 15:39:24

标签: java multithreading concurrency concurrenthashmap

出于并发/多线程学习的目的,我正在开发一个小型汇款API,该API将由多个用户同时调用。我的“数据库”是一个ConcurrentHashMap<String, Double>,其中的键/值对代表一个帐户ID及其当前余额。

我知道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 :(得分:2)

我认为仅使用具有某些原子类型的ConcurrentHashMap不可能解决此类任务。

想象一下一种情况,将一个帐户中的钱转移到另一个帐户中。在这种情况下,您不需要同时在一个Map元素上同步,而是在两个帐户上同步。这称为交易。因此,您需要做的是实现事务。交易应锁定所有受影响的帐户,并在完成后释放它们。

作为另一种选择,您可以只创建带有事务的线程安全队列,然后依次执行所有事务,并且不需要ConcurrentHashMap同步,但是,这可能与您要研究的部分无关。

答案 1 :(得分:1)

Java内部有许多并发解决方案,为了使用正确的并发解决方案,您需要回答一个简单的问题:我的应用程序大多数时候都在做什么?读或写操作?

如果它执行写入(撤回/存款)操作,我建议使用java.util.concurrent.atomic.DoubleAdder实例而不是Double,这样可以确保线程安全并在写入方面提高应用程序吞吐量。

通常,此类应用程序适合Actors模型。每个帐户都可以由一个演员代表。参与者将支持一些消息类型,例如:提款/存款/总计。 AKKA framework是参与者模型的出色实现。

相关问题