synchronized对spring batch的step原子性基本无效,因其仅作用于单jvm内对象锁,无法约束集群、并行step或jobrepository状态变更;应依赖jobrepository乐观锁、事务边界和幂等设计保障真正原子性。

在 Spring Batch 中,synchronized 无法直接用于保障“步长(Step)执行的原子性”,因为它作用于 JVM 级别的对象锁,而 Spring Batch 的 Step 通常由框架调度、可能跨线程甚至跨 JVM(如分区 Step、远程分片),单纯加 synchronized 不仅无效,还可能引发死锁或严重性能瓶颈。
为什么 synchronized 对 Step 原子性基本无效
Spring Batch 的 Step 执行生命周期由 JobLauncher 和 JobRepository 协调,核心状态(如 STARTING / COMPLETING / FAILED)持久化到数据库。即使你在某个自定义 Tasklet 方法上加 synchronized:
- 只锁住当前 JVM 中该对象的实例,对集群部署完全无约束;
- 无法阻止同一 Job 的多个 Step 实例并发执行(比如并行 Flow 或分区 Step);
- 锁的是业务方法,不是 Step 启动/状态变更行为本身,JobRepository 已提交的状态仍可被其他线程读取和覆盖。
真正保障 Step 原子性的推荐方式
Spring Batch 内置机制已围绕数据库事务与状态一致性设计,应优先使用其原生能力:
-
依赖 JobRepository 的乐观锁:默认开启,每次更新
BATCH_JOB_EXECUTION或BATCH_STEP_EXECUTION时校验VERSION字段,冲突时抛OptimisticLockingFailureException,Job 自动失败回滚; -
Step 级事务边界清晰:配置
TransactionManager,确保ItemReader → ItemProcessor → ItemWriter在同一事务中,写入失败则整个 Step 回滚; -
禁用并发启动相同 Job 实例:通过
JobParametersIncrementer+ 唯一参数(如时间戳、UUID)避免重复触发;若需强制单例运行,可在启动前查JobExplorer判断同参数 Job 是否 RUNNING; -
集群环境用数据库锁表或分布式锁:例如在 Job 启动前执行
SELECT ... FOR UPDATE锁定某控制表记录,或集成 Redisson 的RLock控制全局入口。
什么场景下可以谨慎用 synchronized?
仅限于单机非集群、且必须串行访问的**本地共享资源**,例如:
- 自定义
ItemWriter中写入一个静态计数器或本地缓存(非业务主数据); - 调试用的日志统计器、内存 Map;
- 调用外部非线程安全的 SDK(如老版本 PDFBox),且确定只在一个 Step 的单线程 chunk 内使用。
注意:锁对象建议用 private final Object writeLock = new Object();,避免锁 this 或类对象引发意外竞争。
替代 synchronized 的轻量级同步方案
若需更可控的同步逻辑,可用 Java 并发工具替代裸 synchronized:
-
AtomicInteger/AtomicLong替代计数类同步; -
ConcurrentHashMap替代手动同步的HashMap; -
ReentrantLock配合tryLock(timeout)避免无限等待; - 对 Step 入口做幂等控制:用
JobParameters中的唯一键 + 数据库唯一索引,让重复请求因 DB 约束失败而非靠锁阻塞。
不复杂但容易忽略:Step 的“原子性”本质是状态一致性和事务完整性,不是代码执行顺序。把精力放在正确配置 JobRepository、事务传播行为(PROPAGATION_REQUIRED)、以及幂等设计上,比用 synchronized 更可靠、更可扩展。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











