【问题标题】:java.util.ConcurrentModificationException - ArrayListjava.util.ConcurrentModificationException - ArrayList
【发布时间】:2014-06-12 15:46:20
【问题描述】:

开始编辑请向下滚动查看更新的代码END OF EDIT

我已经用谷歌搜索并搜索了为什么会发生此异常,我知道这是由一个对象正在读取列表而同时从列表中删除了一个项目引起的。

我已根据我找到的建议相应地更改了我的代码,但有时我仍然会收到此异常,并且它正在使我的应用程序崩溃。而且它看起来是随机的,我尝试复制异常,并且 90% 的时间我没有得到异常,并且并不总是遵循相同的过程,这使得调试变得困难。

我正在使用观察者模式。有时使用unregister 方法,有时使用register,有时使用notify 中的方法......它发生的位置非常随机。

我正在使用 android asynctask 从我的服务器下载几个字节,观察者模式是在需要时更新 GUI。

这是我的代码:

@Override
    public void register(final Observer newObserver) {
        Log.d(TAG, "(Register) Observer registred: " + newObserver.toString());
        observers.add(newObserver);

        Log.d(TAG, "(Register) Number of registered observers: " + observers.size());

    }

    @Override
    public void unregister(final Observer observer) {

        int indexObersver = observers.indexOf(observer);

        // Avoid java.util.ConcurrentModificationException 
        // at java.util.ArrayList$ArrayListIterator.next(ArrayList.java)

        if(indexObersver >= 0)
        {
            observers.remove(indexObersver);
            Log.d(TAG, "(Unregister) Unregistered Observer: " + observer.toString());
            Log.d(TAG, "(Unregister) Now we have: " + observers.size() + " observers");
        }
        else
        {
            Log.d(TAG, "(Unregister) Registered Observer not found");
        }
    }

    @Override
    public void notifyObserverNewLocalBackup(BackupInfo backupInfo) {

        // Avoid java.util.ConcurrentModificationException 
        // at java.util.ArrayList$ArrayListIterator.next(ArrayList.java)

        for( Iterator< Observer > it = observers.iterator(); it.hasNext() ; )
//      for(Observer observer : observers)
        {
            Observer observer = it.next();
            observer.notifyNewLocalBackup(backupInfo);
        }

    }

    @Override
    public void notifyObserverNewRemoteBackup(ArrayList<PhoneBackup> phoneBackups) {

        // Avoid java.util.ConcurrentModificationException 
        // at java.util.ArrayList$ArrayListIterator.next(ArrayList.java)

//      for(Observer observer : observers)
        for( Iterator< Observer > it = observers.iterator(); it.hasNext() ; )
        {
            Observer observer = it.next();
            observer.notifyNewRemoteBackup(phoneBackups);
        }
    }

    @Override
    public void notifyObserverDownloadCompleted(PhoneBackup phoneBackup) {

        // Avoid java.util.ConcurrentModificationException 
        // at java.util.ArrayList$ArrayListIterator.next(ArrayList.java)

//      for(Observer observer : observers)
        for( Iterator< Observer > it = observers.iterator(); it.hasNext() ; )
        {
            Observer observer = it.next();
            observer.notifyDownloadCompleted(phoneBackup);
        }

    }

    @Override
    public void notifyObserverUploadCompleted(boolean isSucccess) {

        // Avoid java.util.ConcurrentModificationException 
        // at java.util.ArrayList$ArrayListIterator.next(ArrayList.java)

//      for(Observer observer : observers)
        for( Iterator< Observer > it = observers.iterator(); it.hasNext() ; )
        {
            Observer observer = it.next();
            observer.notifyUploadCompleteted(isSucccess);
        }
    }

现在我上次在notifyObserverNewRemoteBackup 方法的Observer observer = it.next(); 行上得到了excption

06-12 04:31:58.394: W/dalvikvm(31358): threadid=1: thread exiting with uncaught exception (group=0x418fcce0)
06-12 04:31:58.629: E/AndroidRuntime(31358): FATAL EXCEPTION: main
06-12 04:31:58.629: E/AndroidRuntime(31358): Process: com.mypackage.android.design.appdesgin, PID: 31358
06-12 04:31:58.629: E/AndroidRuntime(31358): java.util.ConcurrentModificationException
06-12 04:31:58.629: E/AndroidRuntime(31358):    at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:573)
06-12 04:31:58.629: E/AndroidRuntime(31358):    at com.mypackage.android.design.appdesgin.asynctasks.ObserverSubjectManager.notifyObserverNewRemoteBackup(ObserverSubjectManager.java:99)
06-12 04:31:58.629: E/AndroidRuntime(31358):    at com.mypackage.android.design.appdesgin.asynctasks.BackupsHandler$1.success(BackupsHandler.java:318)
06-12 04:31:58.629: E/AndroidRuntime(31358):    at com.mypackage.android.design.appdesgin.asynctasks.BackupsHandler$1.success(BackupsHandler.java:1)
06-12 04:31:58.629: E/AndroidRuntime(31358):    at retrofit.CallbackRunnable$1.run(CallbackRunnable.java:45)
06-12 04:31:58.629: E/AndroidRuntime(31358):    at android.os.Handler.handleCallback(Handler.java:733)
06-12 04:31:58.629: E/AndroidRuntime(31358):    at android.os.Handler.dispatchMessage(Handler.java:95)
06-12 04:31:58.629: E/AndroidRuntime(31358):    at android.os.Looper.loop(Looper.java:136)
06-12 04:31:58.629: E/AndroidRuntime(31358):    at android.app.ActivityThread.main(ActivityThread.java:5081)
06-12 04:31:58.629: E/AndroidRuntime(31358):    at java.lang.reflect.Method.invokeNative(Native Method)
06-12 04:31:58.629: E/AndroidRuntime(31358):    at java.lang.reflect.Method.invoke(Method.java:515)
06-12 04:31:58.629: E/AndroidRuntime(31358):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
06-12 04:31:58.629: E/AndroidRuntime(31358):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:607)
06-12 04:31:58.629: E/AndroidRuntime(31358):    at dalvik.system.NativeStart.main(Native Method)

---------- 编辑 --------- ----------

我遵循了 Anubian Noob 的建议,并实施了一个同步列表,但我仍然遇到异常。

这是我更新的代码:

// Singleton
    public synchronized static ObserverSubjectManager getInstance()
    {
        if(instance == null)
        {
            instance = new ObserverSubjectManager();

            return instance;
        }
    return instance;
}


private ObserverSubjectManager()
{
//      observers = new ArrayList<>();  



    observers = Collections.synchronizedList(new ArrayList<Observer>());
}


@Override
public void register(final Observer newObserver) {
    Log.d(TAG, "(Register) Observer registred: " + newObserver.toString());

    synchronized (observers) {
        observers.add(newObserver);
    }


    Log.d(TAG, "(Register) Number of registered observers: " + observers.size());

}

@Override
public void unregister(final Observer observer) {

    synchronized (observers) 
    {
        int indexObersver = observers.indexOf(observer);

        if(indexObersver >= 0)
        {
            observers.remove(indexObersver);
            Log.d(TAG, "(Unregister) Unregistered Observer: " + observer.toString());
            Log.d(TAG, "(Unregister) Now we have: " + observers.size() + " observers");
        }
        else
        {
            Log.d(TAG, "(Unregister) Registered Observer not found");
        }
    }


}

@Override
public void notifyObserverNewLocalBackup(final BackupInfo backupInfo) {

    synchronized (observers) 
    {
        for(Observer observer : observers)
        {
            observer.notifyNewLocalBackup(backupInfo);
        }
    }


}

@Override
public void notifyObserverNewRemoteBackup(final ArrayList<PhoneBackup> phoneBackups) {

    synchronized (observers) 
    {
        for(Observer observer : observers)
        {
            observer.notifyNewRemoteBackup(phoneBackups);
        }
    }
}

@Override
public void notifyObserverDownloadCompleted(final PhoneBackup phoneBackup) {

    synchronized (observers) 
    {
        for(Observer observer : observers)
        {
            observer.notifyDownloadCompleted(phoneBackup);
        }
    }
}

@Override
public void notifyObserverUploadCompleted(final boolean isSucccess) {

    synchronized (observers) 
    {
        for(Observer observer : observers)
        {
            observer.notifyUploadCompleteted(isSucccess);
        }
    }
}

堆栈跟踪:

06-12 05:12:49.359: W/dalvikvm(31735): threadid=1: thread exiting with uncaught exception (group=0x418fcce0)
06-12 05:12:49.426: E/AndroidRuntime(31735): FATAL EXCEPTION: main
06-12 05:12:49.426: E/AndroidRuntime(31735): Process: com.mypackage.android.design.appdesgin, PID: 31735
06-12 05:12:49.426: E/AndroidRuntime(31735): java.util.ConcurrentModificationException
06-12 05:12:49.426: E/AndroidRuntime(31735):    at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:573)
06-12 05:12:49.426: E/AndroidRuntime(31735):    at com.mypackage.android.design.appdesgin.asynctasks.ObserverSubjectManager.notifyObserverDownloadCompleted(ObserverSubjectManager.java:126)
06-12 05:12:49.426: E/AndroidRuntime(31735):    at com.mypackage.android.design.appdesgin.asynctasks.BackupsHandler$2.success(BackupsHandler.java:336)
06-12 05:12:49.426: E/AndroidRuntime(31735):    at com.mypackage.android.design.appdesgin.asynctasks.BackupsHandler$2.success(BackupsHandler.java:1)
06-12 05:12:49.426: E/AndroidRuntime(31735):    at retrofit.CallbackRunnable$1.run(CallbackRunnable.java:45)
06-12 05:12:49.426: E/AndroidRuntime(31735):    at android.os.Handler.handleCallback(Handler.java:733)
06-12 05:12:49.426: E/AndroidRuntime(31735):    at android.os.Handler.dispatchMessage(Handler.java:95)
06-12 05:12:49.426: E/AndroidRuntime(31735):    at android.os.Looper.loop(Looper.java:136)
06-12 05:12:49.426: E/AndroidRuntime(31735):    at android.app.ActivityThread.main(ActivityThread.java:5081)
06-12 05:12:49.426: E/AndroidRuntime(31735):    at java.lang.reflect.Method.invokeNative(Native Method)
06-12 05:12:49.426: E/AndroidRuntime(31735):    at java.lang.reflect.Method.invoke(Method.java:515)
06-12 05:12:49.426: E/AndroidRuntime(31735):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
06-12 05:12:49.426: E/AndroidRuntime(31735):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:607)
06-12 05:12:49.426: E/AndroidRuntime(31735):    at dalvik.system.NativeStart.main(Native Method)

【问题讨论】:

  • 我的猜测是另一个线程可能正在注册/注销观察者,这会触发ConcurrentModificationException。您是否在日志中看到任何关于观察者在 ConcurrentModificationException 之前注册/取消注册的信息?
  • 就我个人而言,我会说您在迭代期间迭代和更改数组列表的内容。虽然我看不出你现在给出的代码量在哪里
  • 对于初学者,只需使用增强的for 循环。这不是您的问题,但您的代码由于手动执行而更难阅读。
  • @chrylis 我正在使用增强的for 循环,正如您在 cmets 中看到的那样,但从我在互联网上阅读的内容来看,因此建议使用迭代器并使用迭代器来访问和修改列表

标签: java android


【解决方案1】:

为了跟进@Rogue 的评论,我将查找您的任何notifynotifyDownloadCompleted() 等)回调实现取消注册观察者的任何实例。很容易发生的是:

1) 您正在迭代一个集合。在该迭代中,您调用其中一个已注册观察者的方法。

2) 在notify 回调中,注册的观察者从进一步的通知中调用unregister

3) 由于您仍处于该迭代循环中,这将导致ConcurrentModificationException,因为您无法在迭代集合时对其进行修改。

您可以通过执行反向循环来解决此问题:

for (int i = collection.size() - 1; i >= 0; i--) {
    collection.get(i).notifyDownloadCompleted();
}

虽然从技术上讲,您仍然可能会遇到一些极端情况,但也不例外。

【讨论】:

  • 我接受这个答案,因为它是最接近我的问题的答案。我的问题确实是我在一个对象未​​注册自己时迭代集合。这个线程问题让我从 greenrobot 转到 EventBus 库。超级灵活且易于使用,我爱上了它!在这种情况下,不再需要对线程使用观察者模式。
【解决方案2】:

问题是你正在从另一个线程访问你的ArrayList,这意味着当你修改它时你会得到那个异常。一个简单的解决方法是将您的 ArrayList 替换为 CopyOnWriteArrayList(这要慢得多),或者使用 Collections.synchronizedList()

制作同步列表:

List<Observer> list = Collection.synchronizedList(new ArrayList<Observer>);

【讨论】:

  • 可能需要注意,仅仅加入Collections.synchronizedList() 本身并不能允许正确的并发行为。
  • 我也这么认为,可能是某个线程正在修改它。所以我做了synchronized (observer) {the for cycles here} 仍然有那个异常,所以我很困惑
  • @Anubian Noob 请检查我编辑的问题。我已经编辑了我的代码以使用同步列表,但我仍然收到异常
【解决方案3】:

如果您不是从多个线程访问集合,而只是想避免在迭代集合时更改集合时出现问题,那么最简单的方法可能是迭代集合的副本:

for (Observer observer : new ArrayList<>(observers)) {
  observer.notifyNewLocalBackup(backupInfo);
}

当然,这意味着创建副本需要一定的开销。

您也可以使用CopyOnWriteArrayList,它也涵盖了从并发线程访问的情况。

【讨论】:

    【解决方案4】:

    在 for/foreach 循环中使用迭代器

    List<String> stringArrayList = new ArrayList<>();
    for (Iterator<String> stringIterator = stringArrayList.iterator(); 
     stringIterator.hasNext(); ) {
       String string = stringIterator.next();
       if (string.equalsIgnoreCase("otherString")) {
       stringIterator.remove();
       }
     }
    

    附:你可以使用这个 lambda 表达式来简化上面的代码

     stringArrayList.removeIf(string -> string.equalsIgnoreCase("otherString"));
    

    【讨论】:

      猜你喜欢
      • 2011-07-05
      • 2013-12-10
      • 2021-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-16
      • 2013-08-26
      • 2015-11-04
      相关资源
      最近更新 更多