jeremie
jeremie

Reputation: 1131

WeakHashMap and Concurrent Modification

I'm reading the Java Doc about the WeakHashMap and I get the basic concept. Because of the GC thread(s) acting in the background, you can get 'unusual behavior', such as a ConcurrentModificationException when iterating and etc.

The thing I don't get is that if the default implementation is not synchronized and does not contain lock in any way, then how come there is no possibility of getting an inconsistent state. Say you have 2 threads. A GC thread deleting some key at a certain index and at same time and at the same index, a user thread is inserting in the array a key value pair.

To me, if there is no synchronization, then there is a high risk of getting a hash map that is inconsistent.

Even worse, doing something like this might actually be super dangerous because v might actually be null.

if (map.contains(k)) {
   V v = map.get(k)
}

Am I missing something?

Upvotes: 3

Views: 1914

Answers (4)

Igor Melnichenko
Igor Melnichenko

Reputation: 145

This class is intended primarily for use with key objects whose equals methods test for object identity using the == operator. Once such a key is discarded it can never be recreated, so it is impossible to do a lookup of that key in a WeakHashMap at some later time and be surprised that its entry has been removed.

So if one uses WeakHashMap for objects whose equals() is based on identity check, all is fine. The first case you mentioned ("A GC thread deleting some key at a certain index and at same time and at the same index, a user thread is inserting in the array a key value pair.") is impossible because as long as the user thread keeps a reference to the key object it cannot be discarded by GC.

And the same stands for the second example:

if (map.contains(k)) {
   V v = map.get(k)
}

You keep reference k so the corresponding object is reachable and cannot be discarded.

But

This class will work perfectly well with key objects whose equals methods are not based upon object identity, such as String instances. With such recreatable key objects, however, the automatic removal of WeakHashMap entries whose keys have been discarded may prove to be confusing.

Upvotes: 0

user2357112
user2357112

Reputation: 280993

The inconsistent state issues you mention do not arise because the GC does not actively restructure WeakHashMaps. When the garbage collector frees the referent of a weak reference, the corresponding entry is not physically removed from the map; the entry merely becomes stale, with no key. At some later point, the entry may be physically removed during some other operation on the map, but the GC won't take on that responsibility.

You can see one Java version's implementation of this design on grepcode.

Upvotes: 5

Izruo
Izruo

Reputation: 2276

Referring to

even if you synchronize on a WeakHashMap [...] it is possible for the size method to return smaller values over time

the javadoc sufficiently explains to me that there is a possibility for an inconsistent state and that it is completely independent from synchronization.

A few examples later, the given example is referred to, too:

for the containsKey method to return true and later false for a given key

So basically, one should never rely on the state of a WeakHashMap. but use it as atomic as possible. The given example should therefore be rephrased to

V v = map.get(k);
if(null != v) {
}

or

Optional.ofNullable(map.get(k)).ifPresent(() -> { } );

Upvotes: 0

shmosel
shmosel

Reputation: 50716

What you're describing is what the documentation explicitly states:

Because the garbage collector may discard keys at any time, a WeakHashMap may behave as though an unknown thread is silently removing entries.

The only mistake you're making is the assumption that you can protect the state by synchronizing. That doesn't work because the synchronization would not be mutual on the part of the GC. To quote the documentation:

In particular, even if you synchronize on a WeakHashMap instance and invoke none of its mutator methods, it is possible for the size method to return smaller values over time, for the isEmpty method to return false and then true, for the containsKey method to return true and later false for a given key, for the get method to return a value for a given key but later return null, for the put method to return null and the remove method to return false for a key that previously appeared to be in the map, and for successive examinations of the key set, the value collection, and the entry set to yield successively smaller numbers of elements.

Upvotes: 0

Related Questions