0

I'm getting a ConcurrentModificationException while iterating through a HashMap.

How can I fix this?

public void stopAllPlaying(int fadeDurationInMs)
{
    for(PlayThread thread : threadMap.values()) {
        if(thread != null) {
            thread.fadeOut(fadeDurationInMs);
        }
    }
    threadMap.clear();
}

UPDATE:

Unfortunately using lock doesn't seem to have fixed it. I've copied the error message below

09-06 01:05:51.366: E/AndroidRuntime(1307): FATAL EXCEPTION: main
09-06 01:05:51.366: E/AndroidRuntime(1307): java.util.ConcurrentModificationException
09-06 01:05:51.366: E/AndroidRuntime(1307):     at java.util.HashMap$HashIterator.nextEntry(HashMap.java:796)
09-06 01:05:51.366: E/AndroidRuntime(1307):     at java.util.HashMap$ValueIterator.next(HashMap.java:828)
09-06 01:05:51.366: E/AndroidRuntime(1307):     at com.example.myapp.AudioTrackPlayer.stopAllPlaying(AudioTrackPlayer.java:141)
09-06 01:05:51.366: E/AndroidRuntime(1307):     at com.example.myapp.MyApp.stopAllSounds(MyApp.java:119)
09-06 01:05:51.366: E/AndroidRuntime(1307):     at com.example.myapp.MainActivity.stopPlayback(MainActivity.java:114)
09-06 01:05:51.366: E/AndroidRuntime(1307):     at com.example.myapp.MainActivity.onConfigurationChanged(MainActivity.java:451)
09-06 01:05:51.366: E/AndroidRuntime(1307):     at android.app.ActivityThread.performConfigurationChanged(ActivityThread.java:3397)
09-06 01:05:51.366: E/AndroidRuntime(1307):     at android.app.ActivityThread.handleConfigurationChanged(ActivityThread.java:3542)
09-06 01:05:51.366: E/AndroidRuntime(1307):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1121)
09-06 01:05:51.366: E/AndroidRuntime(1307):     at android.os.Handler.dispatchMessage(Handler.java:99)
09-06 01:05:51.366: E/AndroidRuntime(1307):     at android.os.Looper.loop(Looper.java:150)
09-06 01:05:51.366: E/AndroidRuntime(1307):     at android.app.ActivityThread.main(ActivityThread.java:4263)
09-06 01:05:51.366: E/AndroidRuntime(1307):     at java.lang.reflect.Method.invokeNative(Native Method)
09-06 01:05:51.366: E/AndroidRuntime(1307):     at java.lang.reflect.Method.invoke(Method.java:507)
09-06 01:05:51.366: E/AndroidRuntime(1307):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
09-06 01:05:51.366: E/AndroidRuntime(1307):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
09-06 01:05:51.366: E/AndroidRuntime(1307):     at dalvik.system.NativeStart.main(Native Method)

UPDATE 2:

I needed to add the lock to all instances where the Hashmap was being modified!

2
  • Have you tried any synchronizing techiques? "synchronized" keyword, Lock, Semaphore, anything? Commented Sep 6, 2013 at 5:58
  • See my updated answer. I guess you missed a usage. Commented Sep 6, 2013 at 8:33

2 Answers 2

1
private final Lock lock = new ReentrantLock();
public void stopAllPlaying(int fadeDurationInMs)
{
    lock.lock();
    try{
      for(PlayThread thread : threadMap.values()) {
          if(thread != null) {
              thread.fadeOut(fadeDurationInMs);
          }
      }
      threadMap.clear();
    }
    finally{
    lock.unlock();
    }
}

This should do the job.

You must lock all other usages of threadMap in the same way with the same Lock instance.

NOTE: This is not the only way to do synchronization.

See also http://developer.android.com/reference/java/util/concurrent/locks/Lock.html

You may find this helpful, too: http://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html

Sign up to request clarification or add additional context in comments.

14 Comments

Thanks Fildor - this works great. Any idea how efficient this is? Would locking the thread itself be better so wouldn't block my UI thread?
More efficient would be to use some lock-free algorithm. Personally I consider using a Lock pretty efficient as long as the locked block is small enough (in LOC and execution time). At least it is more efficient than locking the whole instance using the synchronized keyword.
I do not think thread.fadeOut is the problem. threadMap.clear() changes the datastructure. So if Thread2 is already iterating on that and Thread1 clears it, the iterator will throw the exception. With the lock, you make sure it is processed once, then cleared. The second Thread will not enter the loop, because threadMap has no elements at that time.
Good find of those two. SynchronizedMap will not help you since it only syncs single calls. The iterator will still "fail fast". About ConcurrentHashMap, I'm not sure. If I remember correctly it will at least not throw an exception.
Docu sais " Iterators and Enumerations return elements reflecting the state of the hash table at some point at or since the creation of the iterator/enumeration. They do not throw ConcurrentModificationException. However, iterators are designed to be used by only one thread at a time." So, you may not get an exception but work on an outdated state.
|
0

You cannot lock the GUI thread for long as this stalls all application. "fadeOut" does not sound as something instant.

Cascade the two threads: GUI thread calls the intermediate thread and this intermediate thread then calls stopAllPlaying, waiting for the lock if necessary. The intermediate thread can wait for arbitrary long time.

1 Comment

Thanks Audruis but I'm not sure I understand what you mean. Could you kindly provide a code example?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.