并发修改exception:添加到ArrayList

问题发生在

Element element = it.next(); 

包含该行的这段代码在OnTouchEvent

 for (Iterator<Element> it = mElements.iterator(); it.hasNext();){ Element element = it.next(); if(touchX > element.mX && touchX < element.mX + element.mBitmap.getWidth() && touchY > element.mY && touchY < element.mY + element.mBitmap.getHeight()) { //irrelevant stuff.. if(element.cFlag){ mElements.add(new Element("crack",getResources(), (int)touchX,(int)touchY)); element.cFlag = false; } } } 

所有这些都在synchronized(mElements)里面,其中mElements是一个ArrayList<Element>

当我触摸一个Element ,它可能会激活cFlag ,它会创build另一个具有不同属性的Element ,这些属性会在不到一秒的时间内从屏幕上消失。 这是我创造粒子效果的方法。 我们可以调用这个“粒子”的crack ,就像构造函数中的String参数一样。

这一切工作正常,直到我添加另一个主Element 。 现在我同时在屏幕上有两个Elements ,如果我触摸最新的Element ,它工作正常,并启动粒子。

但是,如果我触摸并激活旧Element上的cFlag ,则会给我例外。

  07-28 15:36:59.815: ERROR/AndroidRuntime(4026): FATAL EXCEPTION: main 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): java.util.ConcurrentModificationException 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:573) 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at com.Juggle2.Panel.onTouchEvent(Panel.java:823) 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at android.view.View.dispatchTouchEvent(View.java:3766) 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863) 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863) 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1767) 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1119) 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at android.app.Activity.dispatchTouchEvent(Activity.java:2086) 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1751) 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at android.view.ViewRoot.handleMessage(ViewRoot.java:1785) 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at android.os.Handler.dispatchMessage(Handler.java:99) 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at android.os.Looper.loop(Looper.java:123) 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at android.app.ActivityThread.main(ActivityThread.java:4627) 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at java.lang.reflect.Method.invokeNative(Native Method) 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at java.lang.reflect.Method.invoke(Method.java:521) 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:893) 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:651) 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at dalvik.system.NativeStart.main(Native Method) 

我怎样才能做这个工作?

当您使用Iterator遍历列表时修改列表(通过添加或删除元素)时发生ConcurrentModificationException 。

尝试

 List<Element> thingsToBeAdd = new ArrayList<Element>(); for(Iterator<Element> it = mElements.iterator(); it.hasNext();) { Element element = it.next(); if(...) { //irrelevant stuff.. if(element.cFlag){ // mElements.add(new Element("crack",getResources(), (int)touchX,(int)touchY)); thingsToBeAdd.add(new Element("crack",getResources(), (int)touchX,(int)touchY)); element.cFlag = false; } } } mElements.addAll(thingsToBeAdd ); 

Jonbuild议你也应该考虑增强每个循环。

我通常使用这样的东西:

 for (Element element : new ArrayList<Element>(mElements)) { ... } 

快速,干净,无bug

另一种select是使用CopyOnWriteArrayList

迭代时,不允许添加条目到集合中。

一种方法是在迭代mElements时为新条目创build一个新的List<Element> ,然后将所有新的mElement添加到mElementmElements.addAll(newElements) )。 当然,这意味着你将不会为这些新元素执行循环体 – 这是一个问题吗?

同时,我build议您更新您的代码以使用增强的for循环 :

 for (Element element : mElements) { ... } 

索引for循环也应该工作。

 for (int i = 0; i < collection.size(); i++) 

在这种情况下从列表中添加导致CME,没有任何数量的synchronized会让你避免这种情况。 相反,考虑使用迭代器添加…

  for(ListIterator<Element> it = mElements.listIterator(); it.hasNext();){ Element element = it.next(); if(touchX > element.mX && touchX < element.mX + element.mBitmap.getWidth() && touchY > element.mY && touchY < element.mY + element.mBitmap.getHeight()) { //irrelevant stuff.. if(element.cFlag){ // mElements.add(new Element("crack",getResources(), (int)touchX,(int)touchY)); it.add(new Element("crack",getResources(), (int)touchX,(int)touchY)); element.cFlag = false; } } } 

另外,我觉得说像…

…问题发生在Element element = it.next();

为了精确起见,以上不能保证。

API文档指出,这种行为不能得到保证,因为一般来说,不可能在存在非同步并发修改的情况下做出任何硬性保证。 快速失败操作尽最大努力抛出ConcurrentModificationException …

使用迭代器还修复了并发问题,如下所示:

 Iterator<Object> it = iterator.next().iterator(); while (it.hasNext()) { it.remove(); } 

那么我已经尝试了所有的方面,在我的情况下,我在一个适配器迭代列表,但由于再次打我告诉我的exception被抛出的消息。 我试着将列表投射到

  = (CopyOnWriteArraylist<MyClass>)mylist.value; 

但它也给我一个例外的CouldNotCastException,(我终于思考了为什么他们使用或提供了一个铸造的实体)。

我甚至使用了所谓的同步块,但即使它没有奏效,否则我可能会以错误的方式使用它。

因此,当我最终使用#all of time#处理try catch中的exception的方法,并且它工作的时候,所以把你的代码放在

 try{ //block }catch(ConcurrentModificationException){ //thus handling my code over here }