Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What will occur if I would use non final ConcurrentHashMap

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?

like image 405
Baurzhan Avatar asked Nov 07 '13 05:11

Baurzhan


2 Answers

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.

like image 165
yshavit Avatar answered Sep 21 '22 12:09

yshavit


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.

like image 43
user2864740 Avatar answered Sep 19 '22 12:09

user2864740