并发修改例外

Cam*_*ium 8 java collections concurrency

我目前正在研究一个多线程应用程序,偶尔也会收到一个同时修改的异常(平均大约每小时一次或两次,但是看似随机的间隔).

有缺陷的类本质上是一个map的包装器 - 它扩展了LinkedHashMap(accessOrder设置为true).该类有几个方法:

synchronized set(SomeKey key, SomeValue val)
Run Code Online (Sandbox Code Playgroud)

set方法将键/值对添加到内部映射,并受synchronized关键字的保护.

synchronized get(SomeKey key)
Run Code Online (Sandbox Code Playgroud)

get方法根据输入键返回值.

rebuild()
Run Code Online (Sandbox Code Playgroud)

内部地图偶尔重建一次(〜每2分钟,间隔与异常不匹配).rebuild方法基本上根据键重建值.由于rebuild()相当昂贵,我没有在方法上放置synchronized关键字.相反,我正在做:

public void rebuild(){
  /* initialization stuff */
  List<SomeKey> keysCopy = new ArrayList<SomeKey>();
  synchronized (this) {
    keysCopy.addAll(internalMap.keySet());
  }
  /* 
    do stuff with keysCopy, update a temporary map
   */    
  synchronized (this) {
    internalMap.putAll(tempMap);
  }
}
Run Code Online (Sandbox Code Playgroud)

例外情况发生在

keysCopy.addAll(internalMap.keySet());
Run Code Online (Sandbox Code Playgroud)

在synchronized块内.

建议非常感谢.请随意向我指出Effective Java和/或Concurrency in Practice中的特定页面/章节.

更新1:

消毒堆栈跟踪:

java.util.ConcurrentModificationException
        at java.util.LinkedHashMap$LinkedHashIterator.nextEntry(LinkedHashMap.java:365)
        at java.util.LinkedHashMap$KeyIterator.next(LinkedHashMap.java:376)
        at java.util.AbstractCollection.toArray(AbstractCollection.java:126)
        at java.util.ArrayList.addAll(ArrayList.java:473)
        at a.b.c.etc.SomeWrapper.rebuild(SomeWraper.java:109)
        at a.b.c.etc.SomeCaller.updateCache(SomeCaller.java:421)
        ...
Run Code Online (Sandbox Code Playgroud)

更新2:

感谢大家到目前为止的答案.我认为问题在于LinkedHashMap及其accessOrder属性,尽管我并不完全确定atm(调查).

如果LinkedHashMap上的accessOrder设置为true,并且我访问其keySet然后继续通过addAll将keySet添加到linkedList,这些操作中的任何一个是否会改变顺序(即计入"访问")?

Sea*_*iff 7

如果您使用accessOrder = true构造LinkedHashMap,那么LinkedHashMap.get()实际上会改变LinkedHashMap,因为它将最近访问的条目存储在链接的条目列表的前面.当数组列表使用Iterator进行复制时,可能会调用get().


gub*_*bby 6

此异常通常与同步无关 - 如果在Iterator迭代时修改了Collection,通常会抛出此异常.AddAll方法可能使用迭代器 - 值得注意的是,posh foreach循环也会迭代Iterator的实例.

例如:

for(Object o : objects) {
    objects.remove(o);
}
Run Code Online (Sandbox Code Playgroud)

足以在某些集合上获得异常(例如ArrayList).

詹姆士