Threadsafe暴露keySet()的方式

时间:2011-01-17 02:35:07

标签: java hashset

这必须是一个相当常见的情况,我有一个地图,并希望线程安全地公开其密钥集:

public MyClass {
  Map<String,String> map = // ...
  public final Set<String> keys() {
     // returns key set
  }
}

现在,如果我的“地图”不是线程安全的,那么这是不安全的:

  public final Set<String> keys() {
     return map.keySet();
  }

两者都不是:

  public final Set<String> keys() {
     return Collections.unmodifiableSet(map.keySet());
  }

所以我需要创建一个副本,例如:

  public final Set<String> keys() {
     return new HashSet(map.keySet());
  }

然而,这似乎并不安全,因为该构造函数遍历参数的元素并添加()它们。因此,在进行此复制时,可能会发生ConcurrentModificationException。

那么:

  public final Set<String> keys() {
     synchronized(map) {
       return new HashSet(map.keySet());
     }
  }

似乎是解决方案。这看起来不错吗?

5 个答案:

答案 0 :(得分:4)

除非您计划在地图上同步使用它,否则该解决方案并不是特别有用。在它上进行同步并不会阻止其他人同时在其上调用方法。它只会阻止它们同步它。

最好的解决方案似乎只是首先使用ConcurrentHashMap,如果你知道你需要并发put和删除而有人可能正在迭代。如果类提供的并发行为不是您所需要的,那么您可能只需要使用完全同步的Map。

答案 1 :(得分:3)

好问题。我会使用Google Guava库。更具体地说是com.google.common.collect.ImmutableSet.copyOf(Collection<? extends E>)方法。在文档中,有人说这种方法是线程安全的。

答案 2 :(得分:0)

另一种选择是使用ConcurrentHashMap。它的keySet()是线程安全的,因此可能不需要同步或复制。

答案 3 :(得分:0)

如果您对线程安全迭代器感兴趣,并在迭代过程中使用元素的精确快照,那么请转到下面。

public class ThreadSafeIteratorConcurrentMap
{
    private ConcurrentMap<String, String> itrSafeMap = null;

    public ThreadSafeIteratorConcurrentCollection() {
            itrSafeMap = new ConcurrentHashMap<String, String>
    }

    public void synchronized put(psConference conference, String p_key)
    {
         itrSafeMap.putIfAbsent(p_key, conference);
    }

    public psConference getConference(String p_key)
    {
        return (itrSafeMap.get(p_key));
    }

    public void synchronized remove(String p_key)
    {
        itrSafeMap.remove(p_key);
    }

    public boolean containsKey(String p_key)
    {
        return itrSafeMap.containsKey(p_key);
    }

    // Get the size of the itrSafeMap.
    public int size()
    {
        return itrSafeMap.size();
    }

    public Iterator<String> valueIterator()
    {
        return (itrSafeMap.values().iterator());
    }   

    public Iterator<String> keyIterator()
    {
        return (itrSafeMap.keySet().iterator());
    }

}

然后你想要线程安全的迭代器与元素的精确快照;然后在同步块中使用它,如下所示。

   synchronized(threadSafeIteratorConcurrentMapObject) {

       Iterator<String> keyItr = threadSafeIteratorConcurrentMapObject.keyIterator();
       while(keyItr.hasNext()){
        // Do whatever
       }
   }

如果您不介意在迭代时修改集合;只关注迭代器创建时元素的快照;然后没有同步块你可以使用keyItr。哪个已经是线程安全的;它不会通过ConcurrentModificationException。

答案 4 :(得分:-1)

您可以使用Collections.UnmodifiableMap创建临时地图,然后迭代密钥集。