简体   繁体   English

Threadsafe暴露keySet()的方式

[英]Threadsafe way of exposing keySet()

This must be a fairly common occurrence where I have a map and wish to thread-safely expose its key set: 这必须是一个相当常见的情况,我有一个地图,并希望线程安全地公开其密钥集:

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

Now, if my "map" is not thread-safe, this is not safe: 现在,如果我的“地图”不是线程安全的,那么这是不安全的:

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

And neither is: 而且都不是:

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

So I need to create a copy, such as: 所以我需要创建一个副本,例如:

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

However, this doesn't seem safe either because that constructor traverses the elements of the parameter and add()s them. 但是,这似乎并不安全,因为该构造函数遍历参数的元素并添加()它们。 So while this copying is going on, a ConcurrentModificationException can happen. 因此,在进行此复制时,可能会发生ConcurrentModificationException。

So then: 那么:

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

seems like the solution. 似乎是解决方案。 Does this look right? 这看起来不错吗?

That solution isn't particularly helpful unless you plan to also synchronize on the map everywhere it is used. 除非您计划在地图上同步使用它,否则该解决方案并不是特别有用。 Synchronizing on it doesn't stop someone else from invoking methods on it at the same time. 在它上进行同步并不会阻止其他人同时在其上调用方法。 It only stops them from also being able to synchronize on it. 它只能阻止它们同步它。

The best solution really seems to be just use ConcurrentHashMap in the first place if you know you need concurrent puts and removes while someone may be iterating. 如果你知道你需要并发put和删除而有人可能正在迭代,那么最好的解决方案似乎只是首先使用ConcurrentHashMap If the concurrency behavior that class offers isn't what you need, you'll probably just need to use a fully synchronized Map. 如果类提供的并发行为不是您所需要的,那么您可能只需要使用完全同步的Map。

Good question. 好问题。 I would use Google Guava library. 我会使用Google Guava库。 More specifically com.google.common.collect.ImmutableSet.copyOf(Collection<? extends E>) method. 更具体地说, com.google.common.collect.ImmutableSet.copyOf(Collection<? extends E>)方法。 In documentation it has been said that this method is thread safe. 在文档中,据说这种方法是线程安全的。

If you are interested on thread-safe iterator with exact snapshot of elements through out the iteration process then go for the below. 如果您对线程安全迭代器感兴趣,并且在迭代过程中具有元素的精确快照,那么请转到下面。

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());
    }

}

Then where ever you want thread safe iterator with exact snapshot of elements; 然后你想要线程安全的迭代器与元素的精确快照; then use it in synchronized block like below. 然后在同步块中使用它,如下所示。

   synchronized(threadSafeIteratorConcurrentMapObject) {

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

If you don't mind modification on the collection while iteration; 如果您不介意在迭代时修改集合; only concentrating on snapshot of elements at the time of iterator creation; 只关注迭代器创建时元素的快照; then without synchronization block you can use keyItr. 然后没有同步块你可以使用keyItr。 Which is already thread safe; 哪个已经是线程安全的; it wont through ConcurrentModificationException. 它不会通过ConcurrentModificationException。

Another option would be to use ConcurrentHashMap. 另一种选择是使用ConcurrentHashMap。 Its keySet() is thread safe so there might be no need to synchronize or take a copy. 它的keySet()是线程安全的,因此可能不需要同步或复制。

您可以使用Collections.UnmodifiableMap创建临时Map,然后迭代键集。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM