java.util.ConcurrentModificationException - ArrayList

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24188624/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 10:41:22  来源:igfitidea点击:

java.util.ConcurrentModificationException - ArrayList

javaandroid

提问by dazito

START EDITplease scroll down for the updated code END OF EDIT

开始编辑请向下滚动以获取更新的代码END OF EDIT

I've google and searched around SO for why this exception is occurring and I understand that it is caused by an object is reading a list and meanwhile an item was removed from the list.

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

I've changed my code accordingly to the suggestions I've found but from time to time I still get this exception and it is crashing my app. And it looks randomly, I try to replicate the exception and 90% of the time I don't get the exception and not always following the same procedure, which makes it hard to debug.

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

I'm using the observer pattern. Sometimes it happens with the unregistermethod, some othertimes with the register, other times with a method from the notify... it's pretty random to where it happens.

我正在使用观察者模式。有时它发生在unregister方法上,有时发生在register,其他时候发生在notify……它发生的地方是非常随机的。

I'm using an android asynctaskto download few bytes from my server and the observer pattern is to update the GUI when needed.

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

Here's my code:

这是我的代码:

@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);
        }
    }

Now last time I got the excption it happened on notifyObserverNewRemoteBackupmethod at line Observer observer = it.next();

现在上次我得到异常时,它发生在notifyObserverNewRemoteBackup方法上Observer observer = it.next();

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.success(BackupsHandler.java:318)
06-12 04:31:58.629: E/AndroidRuntime(31358):    at com.mypackage.android.design.appdesgin.asynctasks.BackupsHandler.success(BackupsHandler.java:1)
06-12 04:31:58.629: E/AndroidRuntime(31358):    at retrofit.CallbackRunnable.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)

---------------------- EDIT -------------------------------

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

I've followed Anubian Noob suggestion and I implemented a synchronized list but I'm still getting the exception.

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

Here's my updated code:

这是我更新的代码:

// 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);
        }
    }
}

Stacktrace:

堆栈跟踪:

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.success(BackupsHandler.java:336)
06-12 05:12:49.426: E/AndroidRuntime(31735):    at com.mypackage.android.design.appdesgin.asynctasks.BackupsHandler.success(BackupsHandler.java:1)
06-12 05:12:49.426: E/AndroidRuntime(31735):    at retrofit.CallbackRunnable.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)

采纳答案by Kevin Coppock

To follow up @Rogue's comment, I would look for any instances where any of your notify(notifyDownloadCompleted(), etc.) callback implementations unregister an observer. What can easily happen is that:

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

1) You're iterating over a collection. While in that iteration, you call a method on one of the registered observers.

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

2) That registered observer, in the notifycallback, calls through to unregisteritself from further notifications.

2) 该注册观察者,在notify回调中,调用通过进一步通知取消注册自己。

3) Since you're still in that iteration loop, this will cause a ConcurrentModificationExceptionas you cannot modify a collection while iterating over it.

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

You could fix this by doing a reverse loop:

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

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

Although you could technically still run into some edge cases there, but not an exception.

尽管从技术上讲,您仍然可以在那里遇到一些边缘情况,但也不例外。

回答by Anubian Noob

The problem is that you're accessing your ArrayListfrom another thread, which means when you modify it you get that exception. An easy fix is to replace your ArrayListwith a CopyOnWriteArrayList(which is a lot slower), or to use Collections.synchronizedList().

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

To make a synchronized list:

要制作同步列表:

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

回答by Stefan Walter

If you are not accessing the collection from multiple threads, but only want to avoid problems when changing the collection while iterating over it, probably the easiest way is to iterate over a copy of your collection instead:

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

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

This implies a certain overhead for creating the copy, of course.

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

You can also use a CopyOnWriteArrayList, which covers the case of access from concurrent threads, too.

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

回答by RatneZ

Use Iterator inside of your for/foreach loop

在 for/foreach 循环中使用 Iterator

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

P.S. You can simplify the above code using this lambda expression

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

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