Collections.synchronizedMap

时间:2012-05-19 06:18:48

标签: java

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

4 个答案:

答案 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)

编辑:之前错了。

  1. 在SynchronizedMap实例本身上同步SynchronizedSet
  2. 必须在m上进行同步,以确保在循环中地图不会从您下面改变。
  3. 请记住 - 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           }