forEach Lambda 是否会引发竞态条件?结合代码场景咨询
Great question—let’s break this down step by step, since both ConcurrentModificationException (CME) and race conditions are super common pitfalls when mixing lambdas with shared state or concurrent collection access. Let’s start with your code snippet and unpack the risks:
private class deltaCalculator{ Double valueA; Double valueB; //Init delta volatile Double valueDelta = null; private void calculateMinimum(List<T> dataSource){ dataSource.forEach((entry -> { valueA = entry.getA(); valueB = entry.getB(); // ... 后续可能有基于valueA/valueB计算valueDelta的逻辑 })); } }
1. 为什么会触发ConcurrentModificationException?
First up: CME almost always boils down to iterating over a collection while its structure is being modified (add/remove/clear operations). Here’s how it applies to your code:
- The
forEachmethod uses the collection’s underlying iterator under the hood. IfdataSourceis a thread-unsafe collection likeArrayListorLinkedList, and another thread modifies the collection (adds/removes elements) while yourforEachlambda is running, the iterator will detect this structural change and throw a CME. - The lambda itself isn’t the culprit—this is a collection concurrency issue, not a lambda-specific one. But lambdas can make it easier to overlook shared collection access if you’re not paying close attention.
2. 竞态条件的风险在哪里?
Your concern about race conditions is well-founded, and it stems directly from shared mutable state in your deltaCalculator class:
valueAandvalueBare unsynchronized member variables. If multiple threads callcalculateMinimumon the samedeltaCalculatorinstance, or if the instance is shared across threads, the assignments in the lambda (valueA = entry.getA()) are non-atomic and unguarded. This means one thread could overwritevalueAmid-calculation, leading to incorrect values being used in any subsequent logic forvalueDelta.- While
valueDeltais markedvolatile, that only guarantees visibility of changes across threads—it doesn’t make the calculation ofvalueDeltaatomic. For example, if you have logic likevalueDelta = valueA - valueB, a thread could readvalueA, then another thread modifiesvalueB, resulting in a wrong delta value being stored.
3. 修复方案
Let’s fix these issues with practical, actionable steps:
Fixing ConcurrentModificationException
- Use a thread-safe collection: If
dataSourceneeds to be modified concurrently, switch toCopyOnWriteArrayList(ideal for read-heavy workloads—it creates a snapshot for iteration, so structural changes don’t trigger CME). - Iterate over a copy: If you don’t need real-time updates, create a copy of the collection before iterating:
List<T> safeCopy = new ArrayList<>(dataSource); safeCopy.forEach(entry -> { /* your logic here */ }); - Synchronize collection access: Wrap the
forEachcall in a synchronized block to ensure no other thread modifies the collection during iteration:synchronized(dataSource) { dataSource.forEach(entry -> { /* your logic here */ }); }
Fixing Race Conditions
- Avoid shared mutable state: The simplest fix is to move
valueAandvalueBinto the lambda as local variables (or method-local variables). Local variables are thread-safe because each thread gets its own copy:private void calculateMinimum(List<T> dataSource){ Double minDelta = null; dataSource.forEach(entry -> { Double localA = entry.getA(); Double localB = entry.getB(); Double localDelta = localA - localB; // 示例计算逻辑 if (minDelta == null || localDelta < minDelta) { minDelta = localDelta; } }); // 安全更新valueDelta(如果需要) synchronized(this) { this.valueDelta = minDelta; } } - Synchronize access to member variables: If you must keep
valueA/valueBas member variables, wrap all reads/writes in synchronized blocks or useAtomicReferencefor atomic updates:private final AtomicReference<Double> valueA = new AtomicReference<>(); private final AtomicReference<Double> valueB = new AtomicReference<>(); // 在lambda中: valueA.set(entry.getA()); valueB.set(entry.getB()); - 让deltaCalculator无状态: 为每次计算创建一个新的
deltaCalculator实例,或者将逻辑重构为无成员变量的静态方法。这会彻底消除共享状态的问题。
核心结论
Lambda本身不会导致竞态条件或CME——这些问题来自对共享可变状态的不安全访问和线程不安全的集合操作。通过控制共享数据的访问、使用线程安全集合,或者完全避免共享状态,你可以在并发环境中安全地使用Lambda。
内容的提问来源于stack exchange,提问作者adickinson

