Reputation:
I read somewhere that even if ConcurrentHashMap
is guaranteed to be safe for using in multiple threads it should be declared as final
, even private final
. My questions are the following:
1) Will CocurrentHashMap
still keep thread safety without declaring it as final
?
2) The same question about private keyword. Probably it's better to ask more general question - do public/private
keywords affect on runtime behavior? I understand their meaning in terms of visibility/usage in internal/external classes but what about meaning in the context of multithreading runtime? I believe code like public ConcurrentHashMap
may be incorrect only in coding style terms not in runtime, am I right?
Upvotes: 5
Views: 1578
Reputation: 43391
It might be helpful to give a more concrete example of what I was talking about in the comments. Let's say I do something like this:
public class CHMHolder {
private /*non-final*/ CHMHolder instance;
public static CHMHolder getInstance() {
if (instance == null) {
instance = new CHMHolder();
}
return instance;
}
private ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
public ConcurrentHashMap<String, String> getMap() {
return map;
}
}
Now, this is not thread-safe for a whole bunch of reasons! But let's say that threadA sees a null
value for instance
and thus instantiates the CHMHolder
, and then threadB, by a happy coincidence, sees that same CHMHolder
instance (which is not guaranteed, since there's no synchronization). You would think that threadB sees a non-null
CHMHolder.map
, right? It might not, since there's no formal happens-before edge between threadA's map = new ...
and threadB's return map
.
What this means in practice is that something like CHMHolder.getInstance().getMap().isEmpty()
could throw a NullPointerException
, which would be confusing — after all, getInstance
looks like it should always return a non-null
CHMHolder
, and CHMHolder
looks like it should always have a non-null
map. Ah, the joys of multithreading!
If map
were marked final
, then the JLS bit that user2864740 referenced applies. That means that if threadB sees the same instance that threadA sees (which, again, it might not), then it'll also see the map = new...
action that threadA
did -- that is, it will see the non-null
CHM instance. Once it sees that, CHM's internal thread safety will be enough to ensure safe access.
Upvotes: 4
Reputation: 61935
final
and private
say nothing about the thread-safety (or lack thereof) of the object named by said variable. (They modify the variable, not the object.) Anyway ..
The variable will be consistent across threads if it is a final field:
An object is considered to be completely initialized when its constructor finishes. A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's
final
fields.
The actual ConcurrentHashMap object is "thread safe" insofar as the guarantees it makes. In particular, only single method calls/operations are guaranteed and as such using larger synchronization code may be required .. which is easily controlled if the CHM is only accessible from the object that created it.
Using private
is normally considered good because it prevents other code from "accidently" accessing a variable (and thus the object it names) when they should not. However, the private modifier does not establish the same happens-before guarantee as the final
modifier and is thus orthogonal to thread-safety.
Upvotes: 2