Reputation: 5042
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
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
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
Reputation: 3316
EDITED: Was wrong before.
SynchronizedSet
synchronized on the SynchronizedMap instance itself.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
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