为何Java forEach Lambda会抛出ConcurrentModificationException?
Great question—let’s unpack this. First off, your hunch about static methods and local variables is off the mark here: local variables like allocAccounts are thread-local, so they’re not the issue. The problem boils down to how Java’s collection forEach methods handle iteration, versus how indexed for-loops do it.
Let’s start with the forEach failure
Looking at your stack trace, the exception comes straight from ArrayList.forEach(). If you peek under the hood of ArrayList’s forEach implementation, it uses a fail-fast check:
public void forEach(Consumer<? super E> action) { Objects.requireNonNull(action); final int expectedModCount = modCount; final E[] elementData = (E[]) this.elementData; final int size = this.size; for (int i=0; modCount == expectedModCount && i < size; i++) { action.accept(elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } }
It takes a snapshot of the collection’s modification count (modCount) at the start of iteration. If any structural change (adding/removing elements) happens to the ArrayList while forEach is running, modCount increments. When the loop finishes, it compares the current modCount to the original—if they don’t match, it throws the exception to alert you to potential data inconsistency.
Even though you said each thread has its own Execution copy, there’s a likely catch: either execution.legs() or executionLeg.allocs() is returning a shared collection instance (maybe a shallow copy where the underlying list is still referenced across threads), or it’s a view of another shared collection that’s being modified mid-iteration. That’s what’s triggering the fail-fast check.
Why the regular for-loop doesn’t throw
Your indexed for-loop skips the fail-fast check entirely. It just grabs elements by index directly from the array backing the list. This avoids the exception, but it’s not actually solving the root thread-safety problem—you might still get inconsistent data (like missing elements or duplicates) if the collection is modified concurrently, it just won’t tell you about it.
Proper fixes to consider
If you want to eliminate the exception and ensure thread safety, here are your best bets:
- Switch to thread-safe collections: Replace
ArrayListwithCopyOnWriteArrayListfor thelegsorallocscollections. This class makes a copy of the underlying array whenever a structural change happens, so iterators work on a stable snapshot and never throwConcurrentModificationException. - Use deep copies: Make sure each thread’s
Executioninstance has fully independent copies oflegsandallocs(not just shallow references). That way, modifications in one thread don’t affect others. - Synchronize access: If you can’t change the collection type, wrap both iteration and modification operations in synchronized blocks using a common lock. Just be aware this can hit performance if you have high concurrency.
内容的提问来源于stack exchange,提问作者Shashi Shankar

