a Learner
a Learner

Reputation: 5042

Collections.synchronizedMap

From javadocs

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());
  }

Two queries:

1) Is Set returned by m.keySet() also a collection wrapper or just an unsynchronized set?

EDIT:

2)Is it necessary to synchronize on m in

 synchronized(m) {  // Synchronizing on m, not s!

Can't we synchronize on s instead of m?

Upvotes: 4

Views: 2862

Answers (4)

a Learner
a Learner

Reputation: 5042

Just refining the answer with some sourcecode.

http://www.docjar.com/html/api/java/util/Collections.java.html

1) Yes it is a collection wrapper( an instance of private static class SynchronizedSet ) as indicated in line 2054 from Collections class code.

2051        public Set<K> keySet() {
 2052               synchronized (mutex) {
 2053                   if (keySet==null)
 2054                       keySet = new SynchronizedSet<>(m.keySet(), mutex);
 2055                   return keySet;
 2056               }
 2057           }

Note that this SynchronizedSet uses the same mutex which SynchronizedMap returned by Collections.synchronizedMap(new HashMap()); is using.

2)It is necessary to synchronize it on m instead of s because all operations in returned Set(SynchronizedSet) in Set s = m.keySet(); are synchronized on same mutex (returned Synchronized map) but not on returned set. This can be seen from following points:

a)All operations in Map (SynchronizedMap) returned by Collections.synchronizedMap(new HashMap()); are synchronized on returned map iteslf as mutex as apparent from following code lines as apparent from lines 2004,2010,2035. :

1992       public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
 1993           return new SynchronizedMap<>(m);
 1994       }

SynchronizedMap class is defined as:

1999        private static class SynchronizedMap<K,V>
 2000           implements Map<K,V>, Serializable {
 2001          ...
 2002   
 2003           private final Map<K,V> m;     // Backing Map
 2004           final Object      mutex;        // Object on which to synchronize
               ...
               SynchronizedMap(Map<K,V> m) {
 2007               if (m==null)
 2008                   throw new NullPointerException();
 2009               this.m= m;
 2010               mutex = this;
 2011           }
                ...
 2034           public V put(K key, V value) {
 2035               synchronized (mutex) {return m.put(key, value);}
 2036           }
                ...
            }

b)when we are iterating over map by Iterator i = s.iterator(); we should synchronize it on m instead of s because the operations in returned Set(SynchronizedSet) in Set s = m.keySet(); are synchronized on same mutex (returned Synchronized map) but not on s as shown in line 2054.

2051        public Set<K> keySet() {
 2052               synchronized (mutex) {
 2053                   if (keySet==null)
 2054                       keySet = new SynchronizedSet<>(m.keySet(), mutex);
 2055                   return keySet;
 2056               }
 2057           }

Upvotes: 0

SimonC
SimonC

Reputation: 6718

1) The set returned is a SynchronizedSet that uses the same mutex to lock on.

2) The map does not synchronise on itself, rather a separate mutex object, so synchronizing on the map will not stop other threads in that have access to the map from modifying it. This would only work if all of the code that modified the map, also synchronized on the map. The map does synchronise on itself, but oddly refers to itself using a seperate mutex variable. Therefore synchronising on the map will prevent other modifications to the map via the syncronised wrapper. Note that the same mutex is used in the synronised set returned by the keySet() method.

Upvotes: 3

Chip
Chip

Reputation: 3316

EDITED: Was wrong before.

  1. It is a SynchronizedSet synchronized on the SynchronizedMap instance itself.
  2. It is necessary to synchronize on m to make sure that the map doesn't change from under you when you are in the loop.

Remember - the Set s is just a view backed by the original map. So we want to make sure the map doesn't change.

It is instructive to look at the code. Line 1999 has the declaration of SynchronizedMap.

Upvotes: 1

Affe
Affe

Reputation: 47954

1: Yes it returns a synchronized set that shares a mutex with the Map.

2: Yes you need to hold the lock manually while iterating. If you don't changes can be made in between calls to next() and you'll still have problems. Remember it is part of the specification of HashMap that if another thread, for example does an m.put("foo", "bar"); in between two of your calls to i.next(), then next() will throw ConcurrentModificationException. To prevent this, you lock the whole map so nobody can change it until you're done with the iterator. Locking just the set wouldn't stop anybody from adding to the map.

If you need to iterate while concurrent access may be happening, you should look at implementations of ConcurrentMap to make your life a lot easier.

Upvotes: 5

Related Questions