java.util.ConcurrentModificationException:对hashmap的并发访问

时间:2012-10-03 12:57:11

标签: java concurrency map hashmap

在实现中,我编写了以下代码。 try-catch块在方法

try{
    InputVals iv = task.getInputVals();
    Map<String, String> map = iv.getAllValues();
    String a = map.get("value1");
    String b = map.get("value2");
    String x = funcxy.methodGetX();
    String y = funcxy.methodGetY();
    iv.setValue(xval, x);
    iv.setValue(yval,y);

    String []names = {"name1", "name2", "name3"}
    for(int i = 0; i<names.length; i++ ){
         iv.setValue("name"+i, names[i] );
    }
  }

当我多次发出并发请求时(并不总是),它会抛出“java.util.ConcurrentModificationException:并发访问hashmap”错误。我尝试使用

Map<String, String> map= new ConcurrentHashMap<String, String>();
map = iv.getAllValues();

但这并没有解决问题。你能帮助我,让我知道我犯错误的地方。我无法改变

的实施
InputVals iv = task.getInputVals();

4 个答案:

答案 0 :(得分:1)

我不完全确定你想要实现的目标,但正如其他人已经指出的那样,你可能有多个线程试图同时操纵iv.getAllValues()返回的映射,因此抛出异常

使用ConcurrentHashMap复制地图将起到处理本地副本的作用。但请记住,这样做只会在本地使用它,因此不需要它提供的并发检查。您的代码存在的问题是您实际上并未将数据复制到新地图中。你需要这样做:

Map<String, String> map= new ConcurrentHashMap<String, String>( iv.getAllValues() );

根据您对地图条目进行修改时的需要,最简单的&amp;最快的可能会复制地图并处理本地副本。这将防止任何并发问题。当然,如果其他线程需要访问您更新的信息,则该计划不起作用。

try{
    InputVals iv = task.getInputVals();
    Map<String, String> map = new HashMap<String, String>();
    // copy all map values to a local var
    map.putAll( iv.getAllValues() );
    String a = map.get("value1");
    String b = map.get("value2");
    String x = funcxy.methodGetX();
    String y = funcxy.methodGetY();
    iv.setValue(xval, x);
    iv.setValue(yval,y);

    String []names = {"name1", "name2", "name3"}
    for(int i = 0; i<names.length; i++ ){
         iv.setValue("name"+i, names[i] );
    }
  }

如果不这样做,您需要确保在synchronized块中对地图进行任何调用。但是,如果您在访问此地图的代码中有多个不同的位置,这可能会非常困难和繁琐。

答案 1 :(得分:0)

您必须同步对getInputVals()返回的Map的所有访问权限。它在一个单独的线程中被改变。您将需要以下代码中的某些内容,但同样非常重要的是 lockObj 也必须应用于此地图正在使用的任何其他位置,包括当前正在修改的位置(此代码尚未显示。)

try{
    InputVals iv = task.getInputVals();
    String a;
    String b;

    synchronized (lockOjb) {
        Map<String, String> map = iv.getAllValues();
        a = map.get("value1");
        b = map.get("value2");
    }
    String x = funcxy.methodGetX();
    String y = funcxy.methodGetY();
    iv.setValue(xval, x);
    iv.setValue(yval,y);

    String []names = {"name1", "name2", "name3"}
    for(int i = 0; i<names.length; i++ ){
         iv.setValue("name"+i, names[i] );
    }
  }

这可能会也可能不会,这取决于您是否能够更改此代码。例如,如果它已经在库中发生,您可能只需重新设计代码,使其不是多线程的,至少在访问此映射的任何地方。

答案 2 :(得分:0)

您的InputVals.setValue()做了什么?它是如何定义的?如果您在使用原始变量的引用时在业务逻辑中定义ConcurrentHashMap,则没有用处。您需要注意实际的bean本身。

如果有点像下面那样

Class Inputvals {
  Map<String, String> map = new HashMap <String,String>();
  public void setValue(String a,String b){
    map.put(a,b);
  }
  public Map<String,String> getAllValues(){
    return map;
  }
}

我建议你改变如下

Class Inputvals {
  Map<String, String> map = new ConcurrentHashMap<String,String>();
  public void setValue(String a,String b){
    map.put(a,b);
  }
  ..
  ...

这可以帮助您解决多线程访问问题。

答案 3 :(得分:-1)

您可能需要将地图包装在同步集合

Map<String, String> map = Collections.synchronizedMap(iv.getAllValues());

然后继续访问您的地图。