Thread-safe iteration over key set of a synchronized map

user149408 :

Somewhere in my multi-threaded code I have a map (which gets accessed by multiple threads) declared like this:

private Map<Foo, Integer> fooMap =
    Collections.synchronizedMap(new HashMap<Foo, Integer>());

The same class exposes a public method

public Collection<Foo> getFooList() {
    return fooMap.keySet();
}

Somewhere else in my code I iterate over the collection returned by getFooList(). I am aware that most operations on a synchronized map are thread-safe, the one notable exception being iteration, which must be explicitly synchronized. I have implemented this in the following way:

synchronized(bar.getFooList()) {
    for (Foo foo : bar.getFooList()) {
        // do stuff with foo
    }
}

Every now and then I get a ConcurrentModificationException for the for statement. I am wondering whether I am synchronizing with the wrong class instance—should I synchronize with the map rather than its key set? Then again, I do not really want to expose the whole map to other classes (it is private for a reason).

How do I iterate over the key set in a thread-safe manner, without having to expose the entire map?

tsolakp :

From Javadoc:

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

  Map m = Collections.synchronizedMap(new HashMap());
      ...
  Set s = m.keySet();  // Needn't be in synchronized block
      ...
  synchronized (m) {  // Synchronizing on m, not s!
      Iterator i = s.iterator(); // Must be in synchronized block
      while (i.hasNext())
          foo(i.next());
  }

Failure to follow this advice may result in non-deterministic behavior. The returned map will be serializable if the specified map is serializable.

You can use ConcurrentHashMap which guarantees concurrency on keySet. See here:

The view's iterator is a "weakly consistent" iterator that will never throw ConcurrentModificationException, and guarantees to traverse elements as they existed upon construction of the iterator, and may (but is not guaranteed to) reflect any modifications subsequent to construction.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=147801&siteId=1