I am getting the a ConcurrentModificationException from my cooldown timer. I use a thread to reduce the values every second like this:
public class CoolDownTimer implements Runnable {
@Override
public void run() {
for (String s : playerCooldowns.keySet()) {
playerCooldowns.put(s, playerCooldowns.get(s) - 20);
if (playerCooldowns.get(s) <= 0) {
playerCooldowns.remove(s);
}
}
}
}
So every second it should reduce every players cooldown by 20, but the problem is that I a getting the CME every couple hours while running the program, especially when lots of people are online. How do I make it so that if it is still modifying the list, it will wait until the current operation is done and create a sort of modification queue? Thanks! Here is the stack trace:
2012-06-18 20:59:05 [WARNING] Task of 'SurvivorsClasses' generated an exception
java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:839)
at java.util.HashMap$KeyIterator.next(HashMap.java:874)
at me.zachoooo.survivorclasses.CoolDownManager$CoolDownTimer.run(CoolDownManager.java:13)
at org.bukkit.craftbukkit.scheduler.CraftScheduler.mainThreadHeartbeat(CraftScheduler.java:126)
at net.minecraft.server.MinecraftServer.w(MinecraftServer.java:533)
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:459)
Line 13 is the start of the for loop...
You can't modify collections when using a foreach loop.
You can however iterate over the Map.entrySet()
and do everything you need:
public void run() {
for (Iterator<Map.Entry<String,Integer>> i = playerCooldowns.entrySet().iterator(); i.hasNext();) {
Map.Entry<String,Integer> entry = i.next();
entry.setValue(entry.getValue() - 20); // update via the Map.Entry
if (entry.getValue() <= 0) {
i.remove(); // remove via the iterator
}
}
}
A ConcurrentModificationException
is thrown while you try to modify the contents of your Collection
, at the same time while Iterating
through it.
Read this and this for more discussion on it.
The reason why sometimes it might work out for you is clearly mentioned in the documentation.
The iterators returned by all of this class's "collection view methods" are fail-fast: if
the map is structurally modified at any time after the iterator is created, in any way
except through the iterator's own remove method, the iterator will throw a
ConcurrentModificationException. Thus, in the face of concurrent modification, the
iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic
behavior at an undetermined time in the future.
Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally
speaking, impossible to make any hard guarantees in the presence of unsynchronized
concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a
best-effort basis.
Unlike Array
, Collections
are Checked only during the Compilation time, NOT during the run time, thats the reason you cannot modify the Collection like put() or remove() in the loop.