来自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());
}
两个问题:
1)m.keySet()返回的Set也是一个集合包装器还是一个不同步的集合?
修改
2)是否需要在
中同步m synchronized(m) { // Synchronizing on m, not s!
我们不能在s而不是m上同步吗?
答案 0 :(得分:5)
1:是的,它返回一个与Map共享互斥锁的同步集。
2:是的,您需要在迭代时手动保持锁定。如果你没有在对next()的调用之间进行更改,那么你仍然会遇到问题。请记住,它是HashMap规范的一部分,如果另一个线程,例如在您对m.put("foo", "bar");
的两次调用之间执行i.next()
,那么next()
将抛出ConcurrentModificationException。为了防止这种情况,你可以锁定整个地图,这样在你完成迭代器之前,没有人可以改变它。仅锁定该集合不会阻止任何人添加到地图中。
如果您需要在可能发生并发访问时进行迭代,那么您应该查看ConcurrentMap的实现,以使您的生活更轻松。
答案 1 :(得分:3)
1)返回的集合是SynchronizedSet
,它使用相同的互斥锁来锁定。
2)地图不会自身同步,而是单独的互斥对象,因此在地图上进行同步不会阻止其他有权访问地图的线程进行修改。这只有在修改地图的代码的所有时才会起作用,也会在地图上同步。地图会自动同步,但奇怪地使用单独的{{1}引用自身变量。因此,在地图上进行同步将阻止通过同步包装器对地图进行其他修改。请注意,mutex
方法返回的同步集中使用了相同的互斥锁。
答案 2 :(得分:1)
编辑:之前错了。
SynchronizedSet
。请记住 - Set只是原始地图支持的视图。所以我们要确保地图不会改变。
查看code是有益的。 Line 1999有SynchronizedMap的声明。
答案 3 :(得分:0)
用一些源代码完善答案。
http://www.docjar.com/html/api/java/util/Collections.java.html
1)是的,它是一个集合包装器(私有静态类SynchronizedSet
的一个实例),如line 2054
类代码Collections
中所示。
2051 public Set<K> keySet() {
2052 synchronized (mutex) {
2053 if (keySet==null)
2054 keySet = new SynchronizedSet<>(m.keySet(), mutex);
2055 return keySet;
2056 }
2057 }
请注意,此SynchronizedSet使用Collections.synchronizedMap(new HashMap());
返回的SynchronizedMap正在使用的相同互斥锁。
2)必须在m而不是s上同步它,因为Set s = m.keySet();
中返回的Set(SynchronizedSet)中的所有操作都在同一个互斥锁(返回的同步映射)上同步,但在返回的集合上不同步。这可以从以下几点看出:
a)Collections.synchronizedMap(new HashMap());
返回的Map(SynchronizedMap)中的所有操作在返回的映射iteslf上同步为mutex,如下面的代码行所示,如2004,2010,2035行所示。 :
1992 public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
1993 return new SynchronizedMap<>(m);
1994 }
SynchronizedMap类定义为:
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)当我们通过Iterator i = s.iterator();
迭代map时,我们应该在m而不是s上同步它,因为Set s = m.keySet();
中返回的Set(SynchronizedSet)中的操作在同一个互斥锁上同步(返回同步映射) )但不是如第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 }