Reputation: 9791
Recently I was involved in the discussion where I was given a cache which needs to be implemented to support below operation:
int getData(String key){
if(cache.get(key) !=null){
return cache.get(key);
} else {
int val = getValueFromDB(); // line no. 5
cache.put(key, val); // line no. 6
return val;
}
Now the question is, in a multi-threaded scenario, to implement cache, which would you use: HashMap or ConcurrentHashMap?
My ans: ConcurrentHashMap, as it allows read operation on same and different segment and allows write operations only on different segment.
The argument here by lead was, since key is same, multiple threads will execute to line 5
but only one will perform `line no. 6. Since it is a DB call, it needs to executed just once to put the value on cache if not present.
Me: I can make getData
sync.
Lead: Then why ConcurrentHashMap? Normal HashMap would also do.
Me: I'll put line no. 5
and line no. 6
inside sync block.
Lead: Then multiple threads would wait at the block. When one thread executes and notifies others, next thread would execute the db call.
Now, How can we achieve this? Basically, we do not want to execute multiple db calls. It should be done by one thread with just one call.
Please advise.
Upvotes: 2
Views: 2816
Reputation: 44200
He's right on pretty much everything. The problem you're having is that you're not taking full advantage of the features ConcurrentHashMap
provides.
You're using regular methods of Map
- get()
and put()
- which results in a "check-then-act".
Needless to say, this problem has already been solved: there's a computeIfAbsent
method which does pretty much exactly what you need:
int getData(String key){
return cache.computeIfAbsent(key, k -> getValueFromDB());
}
I had initially suggested to use putIfAbsent
but the problem with this is that the getValueFromDB
function will be evaluated regardless of whether its required.
Upvotes: 6
Reputation: 11300
The answer here is to use ConcurrentHashMap
's computeIfAbsent()
. This method is implemented to get the value for the key from the Map, and if it is absent, to calculate it, given the provided mapping Function
. On ConcurrentHashMap
it will do this atomically.
So in your case, the function would be implemented to fetch the entry from the DB. As this happens atomically, you're guaranteed for it to happen only once.
Like this :
int getData(String key){
return cache.computeIfAbsent(key, k -> getValueFromDb());
}
Upvotes: 7