Search code examples
javaconcurrenthashmap

What is the default behavior of clear in java concurrent hashmap


Internally does it lock all the rows and mark each key to be deleted? So that if another thread want to access a key that is about to be deleted it will provide the right behavior?

Or do we have to synchronized the clear function

synchronized (this) {
    myMap.clear();
}

For example, consider the following

myMap = {1: 1, 2: 1, 3: 1}

//Thread1
myMap.clear()
//Thread2
myMap.compute(1, (k, v) -> {v == null ? 0 : k + 1})

What happens when thread1 execute first and thread2 want to access key1 but key1 is not yet deleted?

Is the result

{}

or

{1: 0}

Solution

  • The behavior of clear() in that context is unspecified1. The javadoc states:

    "For aggregate operations such as putAll and clear, concurrent retrievals may reflect insertion or removal of only some entries."

    Looking at the source code, clear() is implemented by clearing each of the segments one at a time. Each segment is locked while clearing, but there is no lock on the entire map. This means that another thread may add entries to a segment that has just been cleared .... before the overall clear() call returns.

    So, in practice, either of the results / behaviors you propose is possible, depending on the size of maps, the distribution of keys between segments, the version of Java you are using, and ... timing.


    Internally does it lock all the rows and mark each key to be deleted?

    No. Each segment is locked (one at a time) while entries in the segment are removed. (This is done to avoid memory anomalies which might corrupt the segments' hash chains, etcetera)


    Regarding this:

    synchronized (this) {
        myMap.clear();
    }
    

    That will not block other threads from inserting elements while the clear() is in progress. It will just stop two threads (executing the same code) from clearing at the same time.

    If you want to guarantee that clear() clears the map, you would need to wrap the map using Collections.synchronizedMap wrapper, and use that consistently. In practice, that defeats the purpose of using ConcurrentHashMap.


    Follow-up question:

    So potentially there could be infinite loop of clearing right? If another thread keep adding element the size of the map is always > 0, the thread that is trying to clear will keep on running.

    Nope. There will be no infinite loop. The clear() method is not looking at the size of the map.

    What will actually happen is that the clear() call will return and the map won't necessarily be empty.


    1 - On careful rereading, I've realized that the quoted javadoc doesn't directly answer the question. In fact, if you look at the "contract" in the Map.clear() javadoc, it states there that the map will be empty after the call returns. This is implicitly contradicted by the javadoc for the ConcurrentHashMap.clear() javadoc, and explicitly contradicted by what the code actually does.