Synchronization for inverse view of synchronized BiMap

920 Views Asked by At

The Maps.synchronizedBiMap() method states that

it is imperative that the user manually synchronize on the returned map when accessing any of its collection views.

Does this include the inverse() view of the BiMap? For example, if the variables are initialized as in the following example, can invoking inverse.put() from other threads be problematic (e.g. the change is not visible in a get() call on either map or inverse, even if put happened-before get)?

BiMap<Object, Object> map = Maps.synchronizedBiMap(HashBiMap.create());
BiMap<Object, Object> inverse = map.inverse();

If this is in fact a problem, is there a standard/recommended way of solving this?

// EDIT

Looking at the implementation, it seems like the inverse() of a SynchronizedBiMap is also a SynchronizedBiMap, sharing the same mutex. Does this mean the described problem is non-existent? Confirmation from a Guava Collections expert would be much appreciated ;)

1

There are 1 best solutions below

2
On

No, in this case you don't have to synchronize on inversed map. You cited only a fragment of the documentation, I'll also switch original keySet() with inverse() in the example code:

Returns a synchronized (thread-safe) bimap backed by the specified bimap. In order to guarantee serial access, it is critical that all access to the backing bimap is accomplished through the returned bimap.

It is imperative that the user manually synchronize on the returned map when accessing any of its collection views:

BiMap<Long, String> map = Maps.synchronizedBiMap(
    HashBiMap.<Long, String>create());
//...
BiMap<String, Long> inverse = map.inverse();  // Needn't be in synchronized block
Set<String> set = inverse.keySet();  // Needn't be in synchronized block
//...
synchronized (map) {  // Synchronizing on map, not set!
  Iterator<String> it = set.iterator(); // Must be in synchronized block
  while (it.hasNext()) {
    foo(it.next());
  }
}

Failure to follow this advice may result in non-deterministic behavior.

So when you want a deterministic behavior during iteration over its views (which includes in iterating over inverse view), you have to synchronize on your instance.

In case of .inverse(), as you mentioned, it creates new synchronized bimap using same mutex object, so it synchronizes properly on methods like get or contains.