The JavaDoc for ConcurrentHashMap
states:
Retrieval operations (including get) generally do not block, so may overlap with update operations (including put and remove). Retrievals reflect the results of the most recently completed update operations holding upon their onset. (More formally, an update operation for a given key bears a happens-before relation with any (non-null) retrieval for that key reporting the updated value.)
Since "an update operation for a given key... happens-before... any (non-null) retrieval for that key" and a partially constructed object is considered non-null,* would the following code potentially allow Thread2
to access partially constructed objects?
Thread1
:
// Immutable Object (all fields final)
concurrentHashMap.put("immutableObject", new ImmutableObject());
// Volatile Object (all fields volatile)
concurrentHashMap.put("volatileObject", new VolatileObject());
// Thread-safe Mutable Object
concurrentHashMap.put("mutableObject", new MutableObject());
Thread2
:
concurrentHashMap.get("immutableObject");
concurrentHashMap.get("volatileObject");
concurrentHashMap.get("mutableObject");
Is there any need to perform some kind of synchronization in the constructors of these objects to ensure that no thread accesses them before they are fully initialized?
* I'm not 100% sure that partially constructed object is considered non-null, but I haven't seen any evidence to the contrary. It seems like since threads can access partially initialized objects, partially initialized objects must not be null since some of the internal data (whatever is initialized) is accessible.
In your example, you will never have partially constructed objects added to the map.
Arguments are evaluated before being passed to a method.
The 15.12.4. Run-Time Evaluation of Method Invocation JLS states indeed that the argument expressions (second step) are evaluated before the method execution (last step) :
At run time, method invocation requires five steps. First, a target reference may be computed. Second, the argument expressions are evaluated. Third, the accessibility of the method to be invoked is checked. Fourth, the actual code for the method to be executed is located. Fifth, a new activation frame is created, synchronization is performed if necessary, and control is transferred to the method code.
So here :
concurrentHashMap.put("immutableObject", new ImmutableObject());
new ImmutableObject()
is evaluated and the ImmutableObject
object is fully build before that it is passed to the concurrentHashMap.put(..)
method.