我目前正在研究一个多线程应用程序,偶尔也会收到一个同时修改的异常(平均大约每小时一次或两次,但是看似随机的间隔).
有缺陷的类本质上是一个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(调查). …
当我执行以下代码时,我得到ConcurrentModificationException
Collection<String> myCollection = Collections.synchronizedList(new ArrayList<String>(10));
myCollection.add("123");
myCollection.add("456");
myCollection.add("789");
for (Iterator it = myCollection.iterator(); it.hasNext();) {
String myObject = (String)it.next();
System.out.println(myObject);
myCollection.remove(myObject);
//it.remove();
}
Run Code Online (Sandbox Code Playgroud)
为什么我得到异常,即使我使用Collections.synchronizedList?
当我将myCollection更改为
ConcurrentLinkedQueue<String> myCollection = new ConcurrentLinkedQueue<String>();
Run Code Online (Sandbox Code Playgroud)
我没有得到那个例外.
java.util.concurrent中的ConcurrentLinkedQueue与Collections.synchronizedList有何不同?
我知道如果使用迭代器在某个线程遍历它时将更改Collection,则iterator.next()将抛出ConcurrentModificationException.
但它根据列表中的元素数量显示不同的行为.
我尝试了一个代码片段,在其中遍历for-each循环中的列表,在它之间,遍历使用列表的remove()方法从列表中删除了一个元素.
理想情况下,它应该在此条件下抛出ConcurrentModificationException而不依赖于列表中的元素数量,但是当列表中的元素数量为2时,它不是真的.
案例1: 列表中的元素数量 - 1
public static void main(String[] args)
{
List<String> list=new ArrayList<String>();
list.add("One");
for (String string : list)
{
System.out.println(string);
list.remove(string);
}
}
Run Code Online (Sandbox Code Playgroud)
输出:一
线程"main"java.util.ConcurrentModificationException中的异常
这是预期的.
案例2:列表中的元素数量 - 2
public static void main(String[] args)
{
List<String> list=new ArrayList<String>();
list.add("One");
list.add("two");
for (String string : list)
{
System.out.println(string);
list.remove(string);
}
}
Run Code Online (Sandbox Code Playgroud)
输出:一
没有例外被抛出?????????
案例3:列表中的元素数量 - 3
public static void main(String[] args)
{
List<String> list=new ArrayList<String>();
list.add("One");
list.add("Two");
list.add("Three");
for (String string …Run Code Online (Sandbox Code Playgroud) 每当我们使用java.util Collection类时,我们就知道如果一个线程更改了一个集合,而另一个线程使用迭代器遍历它,那么任何调用iterator.hasNext()或iterator.next()将抛出ConcurrentModificationException.即使是synchronized集合包装类SynchronizedMap和SynchronizedList仅有条件线程安全的,这意味着所有的个别操作都是线程安全的,但复合操作,其中的控制流取决于前面操作的结果可能会受到线程问题.问题是:如何在不影响性能的情况下避免此问题.注意:我知道CopyOnWriteArrayList.
ConcurrentModificationExcrption当我清除主列表后的子列表时,为什么以下代码抛出,但如果我清除子列表然后清除主列表则不会?
ArrayList<Integer> masterList = new ArrayList<Integer>();
List<Integer> subList;
// Add some values to the masterList
for (int i = 0; i < 10; i++) {
masterList.add(i * i);
}
// Extract a subList from the masterList
subList = masterList.subList(5, masterList.size() - 1);
// The below throws ConcurrentModificationException
masterList.clear();
subList.clear(); // Exception thrown in this line
// The below doesn't throw any exception
subList.clear();
masterList.clear(); // No exception thrown. Confused??
Run Code Online (Sandbox Code Playgroud) 我正在阅读这个" Freuqent Java并发问题 "的问题,并且在谈论java.util.ConcurrentModificationException的答案时感到困惑.
我对答案的理解是,这可能发生在单线程程序中.如何或什么条件导致以下代码抛出异常?
List<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c"));
for (String string : list) { list.remove(string); }
Run Code Online (Sandbox Code Playgroud) 删除第二个最后一个元素时,没有ConcurrentModificationException
List<String> myList1 = new ArrayList<String>();
Collections.addAll(myList1, "str1","str2","str3","str4","str5");
for(String element : myList1){//no ConcurrentModificationException here
if(element.equalsIgnoreCase("str4"))
myList1.remove("str4");
}
System.out.println(myList1);
Run Code Online (Sandbox Code Playgroud)
但是当删除其他元素时会出现ConcurrentModificationException
List<String> myList2 = new ArrayList<String>();
Collections.addAll(myList2, "str1","str2","str3","str4","str5");
for(String element : myList2){//ConcurrentModificationException here
if(element.equalsIgnoreCase("str1"))
myList2.remove("str1");
}
System.out.println(myList2);
Run Code Online (Sandbox Code Playgroud)
是什么原因?
我在我的类中使用了一个Map对象,我已经与LinkedHashMap的Collections.synchronizedMap()同步,如下所示:
private GameObjectManager(){
gameObjects = Collections.synchronizedMap(new LinkedHashMap<String, GameObject>());
}
Run Code Online (Sandbox Code Playgroud)
我在这个函数的第三行得到一个并发修改异常:
public static void frameElapsed(float msElapsed){
if(!INSTANCE.gameObjects.isEmpty()){
synchronized(INSTANCE.gameObjects){
for(GameObject object : INSTANCE.gameObjects.values()){...}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我正在迭代Map的所有其他位置,我按照文档同步地图.
我的类中还有其他函数使用这个Map(同步的!)和put()和remove()对象,但这应该无关紧要.我究竟做错了什么?请询问更多代码,不知道还能提供什么.
哦,和日志消息:
08-20 15:55:30.109: E/AndroidRuntime(14482): FATAL EXCEPTION: GLThread 1748
08-20 15:55:30.109: E/AndroidRuntime(14482): java.util.ConcurrentModificationException
08-20 15:55:30.109: E/AndroidRuntime(14482): at java.util.LinkedHashMap$LinkedHashIterator.nextEntry(LinkedHashMap.java:350)
08-20 15:55:30.109: E/AndroidRuntime(14482): at java.util.LinkedHashMap$ValueIterator.next(LinkedHashMap.java:374)
08-20 15:55:30.109: E/AndroidRuntime(14482): at package.GameObjectManager.frameElapsed(GameObjectManager.java:247)
08-20 15:55:30.109: E/AndroidRuntime(14482): at package.GamekitInterface.render(Native Method)
08-20 15:55:30.109: E/AndroidRuntime(14482): at package.GamekitInterface.renderFrame(GamekitInterface.java:332)
08-20 15:55:30.109: E/AndroidRuntime(14482): at com.qualcomm.QCARSamples.ImageTargets.GameEngineInterface.onDrawFrame(GameEngineInterface.java:107)
08-20 15:55:30.109: E/AndroidRuntime(14482): at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1516)
08-20 15:55:30.109: E/AndroidRuntime(14482): at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1240)
Run Code Online (Sandbox Code Playgroud) java multithreading synchronized linkedhashmap concurrentmodification
请考虑以下代码段:
List<String> list = new LinkedList<>();
list.add("Hello");
list.add("My");
list.add("Son");
for (String s: list){
if (s.equals("My")) list.remove(s);
System.out.printf("s=%s, list=%s\n",s,list.toString());
}
Run Code Online (Sandbox Code Playgroud)
这导致输出:
s = Hello,list = [Hello,My,Son]
s = My,list = [Hello,Son]
很明显,循环只输入两次,第三个元素"Son"永远不会被访问.从底层的库代码中,看起来发生的是hasNext()迭代器中的方法不检查并发修改,只检查下一个索引的大小.由于remove()调用后大小减少了1 ,因此循环不会再次输入,但不会抛出ConcurrentModificationException.
这似乎与迭代器的契约相矛盾:
list-iterator是快速失败的:如果在创建Iterator之后的任何时候对列表进行结构修改,除了通过list-iterator自己的
remove或add方法之外,list-iterator将抛出一个ConcurrentModificationException.因此,在并发修改的情况下,迭代器快速而干净地失败,而不是在未来的未确定时间冒任意,非确定性行为的风险.
这是一个错误吗?同样,迭代器的契约在这里看起来肯定是不服从的 - 列表的结构在迭代过程中由迭代器之外的其他东西进行结构修改.
为什么发生以下异常?
2012-08-28 11:41:59,183 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[localhost].[/TFO].[tfo]] (http-0.0.0.0-8080-9) Servlet.service() for servlet tfo threw exception: java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793) [:1.6.0_24]
at java.util.HashMap$EntryIterator.next(HashMap.java:834) [:1.6.0_24]
at java.util.HashMap$EntryIterator.next(HashMap.java:832) [:1.6.0_24]
at net.sf.json.JSONObject._fromMap(JSONObject.java:1082) [:]
at net.sf.json.JSONObject.fromObject(JSONObject.java:173) [:]
at net.sf.json.JSONObject._processValue(JSONObject.java:2552) [:]
Run Code Online (Sandbox Code Playgroud) java ×10
collections ×5
concurrency ×3
exception ×2
list ×2
hashmap ×1
iterator ×1
linked-list ×1
synchronized ×1