简体   繁体   中英

How to make a ConcurrentHashMap based method thread safe?

For concurrency/multithreading learning purposes, I am developing a small money transfer API which will be concurrently invoked by several users. My "database" is a ConcurrentHashMap<String, Double> , which key/value pair represents an account id and its current balance.

I know that single operations of a ConcurrentHashMap ( get() , put() etc.) are thread-safe, but a withdraw/deposit method would have several method calls that would ultimately make it not thread-safe.

My problem: how to design my withdraw/deposit methods to be thread-safe? At first, I thought about making them synchronized , but that doesn't make any sense as I would be throwing away the fine grained built-in mechanism of synchronization of the ConcurrentHashMap .

These are both my withdraw and deposit methods (don't worry about Double for money here, that's irrelevant in this context):

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);
}

I hope I've made myself clear regarding my issue. Any help would be truly appreciated.

I don't think that it is possible to solve such task just using ConcurrentHashMap with some atomic type.

Imagine a case when money from one account has been transferred to another one. In this case you need to synchronize not on one Map element but on two account simultaneously. This is called transaction. So what you need to do is implement transactions. Transaction should lock all affected acounts and release them after finishing.

As another option you can just create thread-safe queue with transactions and do all transactions sequently and you will not need nor ConcurrentHashMap nither synchronization, however probably this is not about a part which you are trying to study.

Java internals have many solutions for concurrency, in order to use the right one you need answer a simple question: What my application does most of the time? Read or Write operations?

In case it performs writes (withdraw/deposit) I would recommend to use java.util.concurrent.atomic.DoubleAdder instance instead of Double this will ensure the thread safety and increase your application throughput in aspect of writes.

In general, such kind of applications suits to Actors model. Each account can be represented by an actor. The actor will support few message types such: withdraw/deposit/total. AKKA framework is an excellent implementation of actor model.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM