出于并发/多线程学习的目的,我正在开发一个小型的资金转移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条答案
按热度按时间jaxagkaj1#
我不认为仅仅使用某种原子类型的concurrenthashmap就可以解决这样的任务。
想象一下,当一个账户的钱被转到另一个账户时。在这种情况下,您需要同步的不是一个Map元素,而是两个帐户同时进行。这就是所谓的交易。所以您需要做的是实现事务。事务应该锁定所有受影响的帐户,并在完成后释放它们。
作为另一种选择,您可以创建线程安全的事务队列,然后依次执行所有事务,您不需要concurrenthashmap或其他同步,但是这可能不是您要研究的部分。
wz1wpwve2#
java内部有许多并发解决方案,为了使用正确的解决方案,您需要回答一个简单的问题:我的应用程序大部分时间做什么?读或写操作?
如果它执行写操作(取款/存款),我建议使用
java.util.concurrent.atomic.DoubleAdder
示例而不是Double
这将确保线程安全,并在写操作方面提高应用程序吞吐量。一般来说,这类应用程序适合于actors模型。每个帐户可以由一个参与者来代表。actor将支持几种消息类型,例如:draw/deposit/total。akka框架是actor模型的优秀实现。