简体   繁体   English

java.util.ConcurrentModificationException - ArrayList

[英]java.util.ConcurrentModificationException - ArrayList

START EDIT please 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. 我谷歌并搜索SO为什么发生这种异常,我明白这是由一个对象正在读取一个列表,同时一个项目从列表中删除。

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 unregister method, 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 asynctask to download few bytes from my server and the observer pattern is to update the GUI when needed. 我正在使用android asynctask从我的服务器下载几个字节,观察者模式是在需要时更新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 notifyObserverNewRemoteBackup method at line Observer observer = it.next(); 现在,我上次在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$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)

---------------------- 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$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)

To follow up @Rogue's comment, I would look for any instances where any of your notify ( notifyDownloadCompleted() , etc.) callback implementations unregister an observer. 要跟进notifyDownloadCompleted()的评论,我会寻找任何你的notifynotifyDownloadCompleted()等)回调实现取消注册观察者的实例。 What can easily happen is that: 容易发生的是:

1) You're iterating over a collection. 1)你正在迭代一个集合。 While in that iteration, you call a method on one of the registered observers. 在该迭代中,您在其中一个已注册的观察者上调用方法。

2) That registered observer, in the notify callback, calls through to unregister itself from further notifications. 2)注册观察者在notify回调中调用以从进一步通知中注销自己。

3) Since you're still in that iteration loop, this will cause a ConcurrentModificationException as you cannot modify a collection while iterating over it. 3)由于您仍处于该迭代循环中,这将导致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. 虽然你可以在技术上仍然遇到一些边缘情况,但也不例外。

The problem is that you're accessing your ArrayList from another thread, which means when you modify it you get that exception. 问题是你从另一个线程访问你的ArrayList ,这意味着你修改它时会得到那个例外。 An easy fix is to replace your ArrayList with a CopyOnWriteArrayList (which is a lot slower), or to use Collections.synchronizedList() . 一个简单的解决方法是使用CopyOnWriteArrayList (速度慢很多)替换ArrayList ,或者使用Collections.synchronizedList()

To make a synchronized list: 要创建同步列表:

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

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. 您还可以使用CopyOnWriteArrayList ,它也涵盖并发线程访问的情况。

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

PS You can simplify the above code using this lambda expression PS您可以使用此lambda表达式简化上述代码

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 ArrayList上的java.util.ConcurrentModificationException - java.util.ConcurrentModificationException on ArrayList 带有自定义ArrayList的java.util.ConcurrentModificationException - java.util.ConcurrentModificationException with custom ArrayList 多线程ArrayList上的java.util.ConcurrentModificationException - java.util.ConcurrentModificationException on ArrayList in multi thread ArrayList处理中的java.util.ConcurrentModificationException - java.util.ConcurrentModificationException in ArrayList processing arraylist java.util.ConcurrentModificationException从其他类初始化 - arraylist java.util.ConcurrentModificationException initialize from other class 使用INDEX删除ArrayList中的元素,从而导致java.util.ConcurrentModificationException - Removing element in ArrayList using INDEX causing java.util.ConcurrentModificationException 从ArrayList中删除标记:java.util.ConcurrentModificationException - Remove the marker from the ArrayList: java.util.ConcurrentModificationException ArrayList迭代给出异常java.util.ConcurrentModificationException - ArrayList iteration gives exception java.util.ConcurrentModificationException 通过arraylist时抛出java.util.ConcurrentModificationException - java.util.ConcurrentModificationException thrown while going through arraylist 使用 ArrayList 和递归避免 java.util.ConcurrentModificationException - avoid java.util.ConcurrentModificationException using ArrayList and recursion
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM