JMeter负载测试中ArrayList遍历未修改却触发ConcurrentModificationException
Hey there! Let's dig into why you're hitting that tricky ConcurrentModificationException in your JMeter tests—especially since it only pops up in some cases, not all.
What's Causing This?
First off, remember: ArrayList is not thread-safe. JMeter runs tests in a multi-threaded environment, so if one thread is adding elements to your class-level ArrayList (modifying its structure) while another thread uses a Stream to traverse and filter it (reading), the ArrayList's "fail-fast" iterator will throw this exception. The randomness of thread scheduling is why it only happens occasionally—sometimes the read and write operations don't overlap, but when they do, the exception triggers.
Fixes to Try
Here are three practical solutions tailored to different scenarios:
Switch to a thread-safe collection:
CopyOnWriteArrayListis ideal for read-heavy, write-light cases. It creates a copy of the underlying array every time you modify the list, so iterators (including those used by Streams) always work on a stable snapshot. Just update your class variable definition:private CopyOnWriteArrayList<YourElementType> yourList = new CopyOnWriteArrayList<>();No other code changes needed—this handles thread safety automatically.
Add synchronization around list access: If you need to stick with
ArrayList, wrap both the add operation and Stream traversal in synchronized blocks to ensure only one thread modifies or reads the list at a time. For example:// When adding elements synchronized(yourList) { yourList.add(newElement); } // When filtering with Stream synchronized(yourList) { List<YourElementType> filteredList = yourList.stream() .filter(item -> /* your filter condition */) .collect(Collectors.toCollection(ArrayList::new)); }Note: This adds some overhead, so test if it impacts your load test performance.
Traverse a snapshot of the list: Instead of working directly with the class-level list, create a copy before streaming. Even if the original list gets modified during traversal, you'll be safe:
// Create a snapshot first List<YourElementType> listSnapshot = new ArrayList<>(yourList); List<YourElementType> filteredList = listSnapshot.stream() .filter(item -> /* your filter condition */) .collect(Collectors.toList());This is simple, but watch out if your list is large—copying it uses extra memory.
Why Stream Traversal Triggers the Exception
When you call stream() on an ArrayList, it uses the list's default iterator. This iterator tracks the list's modification count; if it detects a structural change (like an add) from another thread mid-traversal, it throws ConcurrentModificationException right away to avoid inconsistent state.
Pick the solution that fits your test's read/write ratio and performance needs, and that annoying exception should vanish!
内容的提问来源于stack exchange,提问作者qwerty

