【问题标题】:ConcurrentModificationException while I'm trying to add an item to a collection尝试将项目添加到集合时出现 ConcurrentModificationException
【发布时间】:2012-01-01 06:40:28
【问题描述】:

我花了一整天的时间试图解决这个问题。我尝试使用迭代?同步,以及许多其他萨满教方法,但我总是得到 ConcurrentModificationException。这里是代码。

package com.androidgui.test;

import java.util.ArrayList;
import java.util.ListIterator;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;

import android.os.Message;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback;
import android.widget.Toast;

public class CustomView extends SurfaceView implements Callback {
    private UpdateThread thread;
    private AButton btn;
    private AWindow wnd;
    private ArrayList<AControl> globalControls;
    private Object sync;
    public static int posX;
    public static int posY;

    public CustomView(Context context) {
        super(context);
        this.getHolder().addCallback(this);
        this.LoadResourse();
        sync =  new Object();
        this.globalControls = new ArrayList<AControl>();
        btn = new AButton(10, 10,null,AControl.InterfaceImages.Button); 
        this.globalControls.add(btn);
        wnd = new AWindow(10, 10, 200, 100, null);
        this.SetDelegates();

    }
    private void LoadResourse()
    {
        AControl.InterfaceImages.Button = BitmapFactory.decodeResource(getResources(), R.drawable.button);
    }
    private void SetDelegates()
    {
        btn.setEventHandler(new EventHandler() {

            @Override
            public void ProcessEvent() {
                synchronized (sync) {
                    globalControls.add(wnd);
                }
            }
        });
    }
    @Override
    public void onSizeChanged(int w, int h, int oldw, int oldh)
    {
        posX = (this.getWidth()- AControl.InterfaceImages.Button.getWidth()) /2;
        posY = (this.getHeight()- AControl.InterfaceImages.Button.getHeight()) /2;
    }

    @Override
    public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
        // TODO Auto-generated method stub

    }

    @Override
    public void surfaceCreated(SurfaceHolder arg0) {
        thread = new UpdateThread(this.getHolder(), this);
        thread.setRunning(true);
        thread.start();

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder arg0) {
        thread.setRunning(false);
        boolean _retry=true;
        try
        {
        while(_retry)
        {
            thread.join();
            _retry=false;
        }
        }catch(Exception ex)
        {

        }
    }
    public boolean onTouchEvent(MotionEvent event)
    {
        synchronized (sync) {
            for(AControl item :this.globalControls)
                item.onClick(event);
        }

        return true;
    }
    public void onDraw(Canvas canvas)
    {
        synchronized (sync) {   
        ArrayList<AControl> temp  = (ArrayList<AControl>) this.globalControls.clone();
            for(AControl item :temp)
                item.onDraw(canvas);
        }
    }

}

【问题讨论】:

    标签: java android


    【解决方案1】:

    在迭代时不能修改集合(从同一个线程或另一个线程)

    您需要制作一个您迭代的副本 (new ArrayList&lt;&gt;(existingList)) 或使用 CopyOnWriteArrayList

    【讨论】:

      【解决方案2】:

      您在此处克隆了您的列表:

       public void onDraw(Canvas canvas)
          {
              synchronized (sync) {   
              ArrayList<AControl> temp  = (ArrayList<AControl>) this.globalControls.clone();
                  for(AControl item :temp)
                      item.onDraw(canvas);
              }
          }
      

      但不是在它之前的方法中:

        public boolean onTouchEvent(MotionEvent event)
          {
              synchronized (sync) {
                  for(AControl item :this.globalControls)
                      item.onClick(event);
              }
      
              return true;
          }
      

      我会说这就是问题所在。但是您没有包含堆栈跟踪的事实使您更难猜测。添加同步不会解决您的问题,因为问题是您可能会在遍历列表时对其进行修改。

      【讨论】:

        【解决方案3】:

        ConcurrentModificationException 的全部含义是,在使用迭代器或增强的 for 循环对集合进行迭代时,您尝试从集合中添加或删除元素。

        例如

        List<String> list = new ArrayList<String>();
        list.add("some string");
        for (String str : list) {
            list.remove(str); // will throw a ConcurrentModificationException
        }
        

        在您的代码中,您的 globalControls 列表中按钮的委托方法似乎在您遍历列表时被调用(也就是说,item.onClickitem.onDraw 将调用您编写的 EventHandler。我注意到如果您正在列表上同步,这在这种情况下对您没有帮助,因为它是访问列表的同一个线程。

        正如 Ravi 之前提到的,这是因为在迭代 List 之前(在 onTouchEvent 中)没有复制 List,因此引发了 ConcurrentModificationException。确保在迭代之前复制列表!

        另一方面,您应该只在尽可能短的时间内同步。

        那是代替

        synchronized (sync) {   
            ArrayList<AControl> temp  = (ArrayList<AControl>) this.globalControls.clone();
                for(AControl item :temp) {
                    item.onDraw(canvas);
                }
        }
        

        你应该写

        ArrayList<AControl> copy;
        synchronized (sync) {   
            copy = (ArrayList<AControl>) globalControls.clone();
            // exit synchronised block as we no longer need to be synchronised 
            // as we have a copy of the list now
        }
        
        for(AControl item : copy) {
            item.onDraw(canvas);
        }
        

        【讨论】:

          猜你喜欢
          • 2020-11-05
          • 2021-12-21
          • 1970-01-01
          • 2021-12-27
          • 1970-01-01
          • 1970-01-01
          • 2019-05-12
          • 2017-05-04
          • 2016-01-26
          相关资源
          最近更新 更多