Please help me figure it out, I think I'm confused. I have a class contains HashMap<String, DateTimeFormatter> that is populated when the application starts.
public class DateService{
private final Map<String, DateTimeFormatter> map;
public PeopleService(String pattern){
map = new HashMap<String, DateTimeFormatter>();
map.put(pattern, DateTimeFormatter.ofPattern(pattern)); //it is only test example
}
}
Should I make it ConcurrentHashMap
or volatile
to convert the date into multiple threads?
A similar question if I want to use List<DateTimeFormatter>
to convert dates in multiple threads
tl;dr
If your intent is a single instance, pre-populated, and unmodified, then the following code should be thread-safe.
If
final
and unmodifiable, you can provide direct access to the map, if that is helpful in your code situation.If you intend to have only a single map created, but may modify its contents, then perhaps this code.
We use
Map.of
here for its convenient literals-syntax, but pass it to a constructor for another map.Notice that we define the member field as
ConcurrentMap
to advertise to the reader its thread-safety, and to double-check our code to ensure we assign only an object that implementsConcurrentMap
.If you intend to possibly replace the entire map with another map object, then use
AtomicReference
(or, alternatively, mark itvolatile
).Notice the
…Ref
added to the member field name to remind the reader of it being an atomic reference, meaning it takes us one step away from the payload we actually care about (the map).We mark the atomic reference as
final
to prevent our member field from ever being assigned to any otherAtomicReference
object. So the payload (the map object’s reference) may be changed out, but its container (theAtomicReference
object) remains fixed.To modify the map stored away inside an atomic reference, go through the atomic.
Caveat: All that code is untested, off the top of my head.
Details
Regarding thread-safety, two considerations:
DateTimeFormatter
objects themselves are guaranteed by the the Javadoc to be thread-safe. Being immutable contributes towards being thread-safe. To quote the Javadoc: Implementation Requirements: This class is immutable and thread-safe.Map
implementation that you (a) populate ahead of usage, and (b) do not modify (no puts, no removes), is thread-safe for retrieval.So key part here, which you did not explain, is your intention to (a) modify or not modify the map, and (b) replace or not replace the map.
Map
. TheHashMap
implementation is not thread-safe under possibly modified. Use aMap
class that implementsConcurrentMap
. Java comes bundled with two such implementations:ConcurrentHashMap
&ConcurrentSkipListMap
. You may find third-party ones, perhaps in Eclipse Collections and/or Google Guava.Only if you intend to modify the elements of the map.
Not if you instantiate (once, and only once) and populate before any possible usage. I personally prefer this approach. Instantiating & populating in a constructor is one good way to do this.
If your intention is to instantiate a single
Map
object, and keep it unmodifiable, then two tips:Map
field asfinal
to ensure that another map object’s reference is never assigned to that variable.Map.of
orMap.copyOf
to ensure your map cannot be modified.If you intend to switch out one map object for another during runtime, then you definitely have an "visibility" issue. In that case, you can either (a) mark the field
volatile
or (b) use anAtomicReference
variable to hold the current map object’s reference. I personally prefer the latter because I like howAtomic…
screams out to the reader that we are protecting a resource under concurrent access. Having to use theAtomic…
methods everywhere keeps us aware of being in a multi-threaded situation.