4

来自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 in 上同步

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

我们不能在 s 而不是 m 上同步吗?

4

4 回答 4

5

1:是的,它返回一个与 Map 共享互斥锁的同步集。

2:是的,您需要在迭代时手动锁定。如果您在调用 next() 之间不进行更改,您仍然会遇到问题。请记住,HashMap 规范的一部分是,如果另一个线程(例如,m.put("foo", "bar"); 在您的两次调用之间进行了一次调用) i.next()next()则将抛出 ConcurrentModificationException。为了防止这种情况,您锁定整个地图,这样在您完成迭代器之前没有人可以更改它。仅锁定集合不会阻止任何人添加到地图中。

如果您需要在可能发生并发访问时进行迭代,您应该查看 ConcurrentMap 的实现以使您的生活更轻松。

于 2012-05-19T06:28:37.463 回答
3

1) 返回的集合是一个SynchronizedSet使用相同互斥锁锁定的集合。

2)映射不会在自身上同步,而是在单独的互斥对象上同步,因此在映射上同步不会阻止有权访问映射的其他线程对其进行修改。这只有在修改地图的所有代码都在地图上同步时才有效。 地图确实在自身上同步,但奇怪的是使用单独的mutex变量引用自身。因此,在地图上同步将防止通过同步包装器对地图进行其他修改。请注意,该方法返回的同步集合中使用了相同的互斥锁keySet()

于 2012-05-19T06:27:51.187 回答
1

编辑:之前错了。

  1. 它是SynchronizedSetSynchronizedMap 实例本身的同步对象。
  2. 有必要在 m 上进行同步,以确保您在循环中时地图不会从您下方更改。

请记住 - Set 只是原始地图支持的视图。所以我们要确保地图不会改变。

查看代码很有启发性。1999 行有 SynchronizedMap 的声明。

于 2012-05-19T06:31:24.693 回答
0

只需使用一些源代码完善答案。

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

1)是的,它是一个集合包装器(私有静态类的一个实例SynchronizedSet),如类代码中所示line 2054Collections

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 使用与所返回的 SynchronizedMap 相同的互斥锁 Collections.synchronizedMap(new HashMap());

2) 必须在 m 而不是 s 上进行同步,因为返回的 Set(SynchronizedSet) 中的所有操作Set s = m.keySet();都在同一个互斥锁(返回的同步映射)上同步,而不是在返回的集合上同步。这可以从以下几点看出:

a) 返回的 Map (SynchronizedMap) 中的所有操作 Collections.synchronizedMap(new HashMap());都在返回的 map iteslf 上同步为互斥锁,从以下代码行可以看出,从 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)当我们通过 map 迭代时,Iterator i = s.iterator(); 我们应该在 m 而不是 s 上同步它,因为返回的 Set(SynchronizedSet) inSet s = m.keySet();中的操作在同一个互斥锁(返回的同步映射)上同步,而不是在 s 上同步,如第 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           }
于 2012-06-14T08:00:54.877 回答