Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/311.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 公开密钥集()的线程安全方式_Java_Hashset - Fatal编程技术网

Java 公开密钥集()的线程安全方式

Java 公开密钥集()的线程安全方式,java,hashset,Java,Hashset,这一定是一个相当常见的情况,我有一个映射,并希望线程安全地公开其密钥集: public MyClass { Map<String,String> map = // ... public final Set<String> keys() { // returns key set } } 公共MyClass{ 映射映射=/。。。 公共最终设置键(){ //返回键集 } } 现在,如果我的“地图”不是线程安全的,那么这是不安全的: public f

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

public MyClass {
  Map<String,String> map = // ...
  public final Set<String> keys() {
     // returns key set
  }
}
公共MyClass{
映射映射=/。。。
公共最终设置键(){
//返回键集
}
}
现在,如果我的“地图”不是线程安全的,那么这是不安全的:

  public final Set<String> keys() {
     return map.keySet();
  }
public final Set key(){
返回map.keySet();
}
这两者都不是:

  public final Set<String> keys() {
     return Collections.unmodifiableSet(map.keySet());
  }
public final Set key(){
返回集合.unmodifiableSet(map.keySet());
}
所以我需要创建一个副本,例如:

  public final Set<String> keys() {
     return new HashSet(map.keySet());
  }
public final Set key(){
返回新的HashSet(map.keySet());
}
但是,这似乎也不安全,因为该构造函数遍历参数的元素并添加它们。因此,在复制过程中,可能会发生ConcurrentModificationException

那么:

  public final Set<String> keys() {
     synchronized(map) {
       return new HashSet(map.keySet());
     }
  }
public final Set key(){
同步(地图){
返回新的HashSet(map.keySet());
}
}

似乎是解决办法。这看起来对吗?

问得好。我会使用谷歌番石榴图书馆。更具体地说,
com.google.common.collect.ImmutableSet.copyOf(Collection该解决方案不是特别有用,除非您计划在地图上使用它的任何地方进行同步。在地图上进行同步不会阻止其他人同时调用地图上的方法。它只会阻止其他人也能够在地图上进行同步


如果您知道在有人进行迭代时需要并发放置和删除,那么最好的解决方案似乎就是首先使用
ConcurrentHashMap
。如果类提供的并发行为不是您需要的,那么您可能只需要使用完全同步的映射。

您可以使用g
Collections.UnmodifiableMap
,然后迭代键集。

另一个选项是使用ConcurrentHashMap.Its键集()是线程安全的,因此可能不需要同步或复制。

如果您对线程安全迭代器感兴趣,在整个迭代过程中提供元素的精确快照,请参阅以下内容

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

}
公共类ThreadSafeIteratorConcurrentMap
{
私有ConcurrentMap itrSafeMap=null;
公共线程安全迭代器ConcurrentCollection(){
itrSafeMap=新的ConcurrentHashMap
}
public void synchronized put(psConference会议,字符串p_键)
{
itrSafeMap.putIfAbsent(p_键,会议);
}
公共psConference getConference(字符串p_键)
{
返回(itrSafeMap.get(p_键));
}
公共无效同步删除(字符串p_键)
{
itrSafeMap.remove(p_键);
}
公共布尔containsKey(字符串p_键)
{
返回itrSafeMap.containsKey(p_键);
}
//获取地图的大小。
公共整数大小()
{
返回itrSafeMap.size();
}
公共迭代器valueIterator()
{
返回(itrSafeMap.values().iterator());
}   
公共迭代器keyIterator()
{
返回(itrSafeMap.keySet().iterator());
}
}
然后,在任何需要线程安全迭代器的地方使用元素的精确快照;然后在如下所示的同步块中使用它

   synchronized(threadSafeIteratorConcurrentMapObject) {

       Iterator<String> keyItr = threadSafeIteratorConcurrentMapObject.keyIterator();
       while(keyItr.hasNext()){
        // Do whatever
       }
   }
synchronized(threadSafeIteratorConcurrentMapObject){
迭代器keyItr=threadSafeIteratorConcurrentMapObject.keyIterator();
while(keyItr.hasNext()){
//做任何事
}
}

如果您不介意在迭代时对集合进行修改;在创建迭代器时只关注元素的快照;那么在没有同步块的情况下,您可以使用keyItr。它已经是线程安全的;它不会通过ConcurrentModificationException进行修改。

文档说,“即使元素是当前正在被另一个线程修改的同步或并发集合,也可以安全地使用此方法”;我没有保证我的“映射”“已同步。@Jake,如果您的映射未同步,则这是一个无法解决的问题。@Jake。我不知道您是否要在运行时修改映射。如果不,则可以使用ImmutableMap。否则,使用ConcurrentHashMap(由Affe发布的解决方案)确实是您的最佳选择。请注意,我不是公开地图本身,而是只公开其密钥集。以及密钥集的副本。是的,我认为这不会改变任何事情?同步该块仍然无法修复您试图修复的危险。如果您担心
ConcurrentModificationException
,则at表示映射以非线程安全的方式公开…是的,抱歉,您是正确的。看起来底线是基础映射需要是线程安全的。如果基础映射不是线程安全的,则这不能防止ConcurrentModificationException。当您有不可修改的集合时,您无法在e底层映射。那么你怎么说它不是线程安全的。@SureshSankar在UnmodifiableMap中使用的真实集合可能会被其他线程rit更改?@Sugumar:UnmodifiableMap阻止添加和删除对象,但可以修改映射中的对象。要防止修改这些对象,请执行深度克隆和启用它,而不是原始对象。