I have a static HashMap
that is initialized on server startup. Clients initialize their data from this map when they login.
Now I need to refresh this map, but
Lets assume that "refresh" means that you want to replace all entries in the hashmap with a fresh set loaded from (say) a file.
If the set of keys in the new mapping is a superset of the keys in the original mapping, AND if you application doesn't care if clients can set part of the old mapping and part of the new mapping at the same time, then you could use a ConcurrentHashMap
instead of a HashMap
, and replace the entries with a sequence of put
calls.
However, if keys are (or could be) different, or if the update needs to be atomic from the client's perspective then a ConcurrentHashMap
is not going to work. Instead, you need to declare map
as a volatile
and implement your refresh()
method as per your question.
As you point out, using synchronized
(or a single-writer-multiple-reader lock) is liable to lead to a concurrency bottleneck.
Note: using a volatile
is likely to give better performance than using a ConcurrentHashMap
even in the cases where the latter is a viable solution.
First of all your map needs to be declared as volatile
in order to ensure that each thread has the last version of it, then here is how you could proceed:
public void refresh() {
synchronized (MyClass.class) {
Map<String, Object> newMap = prepareData();
map = Collections.unmodifiableMap(newMap);
}
}
And your map would be declared as below:
private static volatile Map<String, Object> map = ...
If it's ok that clients have stale data, then all you need to do is create a new map and point your static reference at it. If a new client comes along while you're doing this then they get the stale data and no harm is done, if they turn up after the switch (reassignment) to the new values has occurred then they will get the new values. Job done.
If it's not ok then you will also, probably, have to inform other clients that existed before the update about the change. In which case you want to use the observer pattern for the updates. In this pattern it's fine if the client connects during the update, because they will be updated as soon as possible after the update is complete.
BTW: in all cases, you really shouldn't be using 'static' for anything. It'll only lead to problems down the line. Rather, create a non-static singleton that holds the map and inject that into your clients/services/whatever.