Search code examples
javaandroidrunnableconcurrentmodification

ConcurrentModificationException in a runnable


I am developing a timer manager that will allow multiple countdown timers and I cant seem to figure out how to avoid this ConcurrentModificationException. I have look at other peoples responses to similar problems but still cant figure it out.

mHandler = new Handler();

        mUpdateUI = new Runnable() {
            public void run() {

        Iterator<HashMap.Entry<String, TimerHolder>> it = mTimers.entrySet().iterator();
            while (it.hasNext()) {
   -----------> Map.Entry<String, TimerHolder> pairs = it.next();
                pairs.getValue().post();
            }

            Iterator<HashMap.Entry<String, TimerHolder>> iterator = mTimers.entrySet().iterator();
            while (iterator.hasNext()) {
                HashMap.Entry<String, TimerHolder> entry = iterator.next();
                if (!entry.getValue().isValid()) {
                   iterator.remove();
                }
            }                   


                mHandler.postDelayed(mUpdateUI, 1000); // 1 second
            }
        };
        mHandler.post(mUpdateUI);

06-02 12:37:23.746: E/AndroidRuntime(10669): FATAL EXCEPTION: main
06-02 12:37:23.746: E/AndroidRuntime(10669): java.util.ConcurrentModificationException
06-02 12:37:23.746: E/AndroidRuntime(10669):    at java.util.HashMap$HashIterator.nextEntry(HashMap.java:806)
06-02 12:37:23.746: E/AndroidRuntime(10669):    at java.util.HashMap$EntryIterator.next(HashMap.java:843)
06-02 12:37:23.746: E/AndroidRuntime(10669):    at java.util.HashMap$EntryIterator.next(HashMap.java:841)
06-02 12:37:23.746: E/AndroidRuntime(10669):    at com.watcher.timer.TimerManager$1.run(TimerManager.java:57)
06-02 12:37:23.746: E/AndroidRuntime(10669):    at android.os.Handler.handleCallback(Handler.java:730)
06-02 12:37:23.746: E/AndroidRuntime(10669):    at android.os.Handler.dispatchMessage(Handler.java:92)
06-02 12:37:23.746: E/AndroidRuntime(10669):    at android.os.Looper.loop(Looper.java:137)
06-02 12:37:23.746: E/AndroidRuntime(10669):    at android.app.ActivityThread.main(ActivityThread.java:5493)
06-02 12:37:23.746: E/AndroidRuntime(10669):    at java.lang.reflect.Method.invokeNative(Native Method)
06-02 12:37:23.746: E/AndroidRuntime(10669):    at java.lang.reflect.Method.invoke(Method.java:525)
06-02 12:37:23.746: E/AndroidRuntime(10669):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1209)
06-02 12:37:23.746: E/AndroidRuntime(10669):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1025)
06-02 12:37:23.746: E/AndroidRuntime(10669):    at dalvik.system.NativeStart.main(Native Method)

Solution

  • Actually your issue is not related to concurrency. Your Runnable is always called from the same thread - the Main thread, because you post it to Handler.

    ConcurrentModificationException is thrown because you tries to modify collection inside for-each loop. That's fail-fast behaviour in order to protect non-thread-safe collections from potential concurrent modifications. You need to use Iterator explicitly and call remove at iterator object. Your "deletes invalid entries" part should look like this:

    Iterator<HashMap.Entry<String, TimerHolder>> iterator = mTimers.entrySet().iterator();
    while (iterator.hasNext()) {
        HashMap.Entry<String, TimerHolder> entry = iterator.next();
        if (!entry.getValue().isValid()) {
           iterator.remove();
        }
    }