在类中创建含临界区的任务,是否存在竞态条件问题?
代码可行性分析与竞态条件验证
核心需求确认
- 每次调用
createTasks时生成独立新锁,锁不跨多次调用共享 - 确保
happenOnce方法在每批任务中仅执行一次 - 完全避免竞态条件
基于需求的代码验证(假设你的实现符合如下核心逻辑)
先给出符合需求的参考实现(若你的实际代码与该逻辑一致,则完全可行):
public class TestConcurrency { public List<Runnable> createTasks() { // 每次调用创建全新锁实例,仅服务当前批次任务 final Object batchLock = new Object(); // 用数组存储执行标志,适配lambda对外部变量的访问规则 final boolean[] hasExecuted = {false}; List<Runnable> taskList = new ArrayList<>(); for (int i = 0; i < 10; i++) { taskList.add(() -> { // 业务逻辑前先处理happenOnce的执行控制 synchronized (batchLock) { if (!hasExecuted[0]) { happenOnce(); hasExecuted[0] = true; } } // 其他任务专属逻辑 System.out.println("Task executed: " + Thread.currentThread().getName()); }); } return taskList; } private void happenOnce() { System.out.println("happenOnce executed by: " + Thread.currentThread().getName()); } } // 调用方MyProgram代码 public class MyProgram { public static void main(String[] args) { ExecutorService threadPool = Executors.newFixedThreadPool(5); TestConcurrency concurrencyTest = new TestConcurrency(); // 第一批任务:独立锁控制happenOnce执行一次 threadPool.invokeAll(concurrencyTest.createTasks()); // 第二批任务:全新锁,happenOnce再次执行一次 threadPool.invokeAll(concurrencyTest.createTasks()); threadPool.shutdown(); } }
可行性结论
1. 锁的隔离性满足要求
每次调用createTasks时,都会创建全新的batchLock实例,该锁仅绑定当前批次的任务集合,不会被后续createTasks调用生成的任务共享,完全符合“锁不跨多次调用共享”的要求。
2. 竞态条件完全避免
- 通过
synchronized(batchLock)实现了对hasExecuted标志的原子性检查与修改,同一批次的多个线程竞争锁时,只有第一个获取到锁的线程能执行happenOnce并将标志设为true,后续线程会直接跳过,无竞态风险。 - 使用数组存储
hasExecuted是正确的选择:lambda表达式只能访问外部的final或effectively final变量,数组可以在不改变引用的前提下修改内部值,避免编译错误和逻辑失效。
3. happenOnce执行次数严格受控
- 同一批次的任务共享同一个锁和执行标志,确保
happenOnce仅执行一次。 - 不同批次的任务拥有独立的锁和标志,因此每调用一次
createTasks并执行任务,happenOnce就会执行一次,完全符合预期。
潜在风险提示
- 若将
batchLock或hasExecuted声明为TestConcurrency类的成员变量,会导致锁和标志跨createTasks调用共享,直接违反需求,必须确保它们是createTasks方法内的局部变量。 - 不要用普通布尔变量替代数组:普通布尔变量无法在lambda中修改,会引发编译错误或逻辑错误。
内容的提问来源于stack exchange,提问作者Elad Yehezkel
相关产品推荐
相关产品推荐

