Spring Batch Admin在每次执行后抛出ConcurrentModificationException



随着我对它的深入,我发现我喜欢spring批处理管理应用程序。

但是,在每次执行作业之后,我都会看到抛出此异常。

环境

OS: Windows 7
Java: jdk 1.8.0_25
Spring Batch Admin Sample version: 1.3.1
Spring version: 3.2.13   * stock 3.2.9 has a bug that causes other symptoms 
Spring-batch version: 3.0.2
Pivotal tc version:  3.0 Developer Edition
IDE: STS 3.6.3

日志段:

14:33:36.246 [pool-1-thread-1] ERROR o.s.s.s.TaskUtils$LoggingErrorHandler - Unexpected error occurred in scheduled task.
java.util.ConcurrentModificationException: null
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) ~[na:1.8.0_25]
    at java.util.ArrayList$Itr.remove(ArrayList.java:865) ~[na:1.8.0_25]
    at org.springframework.batch.admin.service.SimpleJobService.removeInactiveExecutions(SimpleJobService.java:498) ~[spring-batch-admin-manager-1.3.1.MAXIS-MOD.jar:na]
    at sun.reflect.GeneratedMethodAccessor196.invoke(Unknown Source) ~[na:na]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_25]
    at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_25]
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:64) ~[spring-context-3.2.13.RELEASE.jar:3.2.13.RELEASE]
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:53) ~[spring-context-3.2.13.RELEASE.jar:3.2.13.RELEASE]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_25]
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) [na:1.8.0_25]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_25]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) [na:1.8.0_25]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_25]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_25]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_25]

我的代码没有触及spring批处理管理的核心,所以我有点困惑。

这还不是一个亮点,所以我愿意接受建议(包括最终贡献一个补丁)。

异常的原因是org.springframework.batch.admin.service.SimpleJobService的错误实现,至少在1.3.1中是这样。RELEASE:

private Collection<JobExecution> activeExecutions = Collections.synchronizedList(new ArrayList<JobExecution>());

public void removeInactiveExecutions() {
            for (Iterator<JobExecution> iterator = activeExecutions.iterator(); iterator.hasNext();) {
                JobExecution jobExecution = iterator.next();
    ...
                if (!jobExecution.isRunning()) {
                    iterator.remove();
                }
            }

java.util.ArrayList上调用iterator.remove();之后调用iterator.getNext();(即使是同步的)不是一个好主意。。。

我目前正在重写Spring Batch Admin配置,使用我自己的JobService for bean实现,id="JobService":

META-INFspringbatchoverrideexecution-context.xml
...
<bean id="jobService" class="com.foo.springbatch.MyPatchedSimpleJobServiceFactoryBean">
    <property name="jobRepository" ref="jobRepository" />
    <property name="jobLauncher" ref="jobLauncher" />
    <property name="jobLocator" ref="jobRegistry" />
    <property name="dataSource" ref="dataSource" />
</bean>
...

刚刚遇到这个问题,一个简单的解决方案是将org.springframework.batch.admin.service.SimpleJobService中的代码更改为:

public void removeInactiveExecutions() {
        **synchronized (activeExecutions) {**
            for (Iterator<JobExecution> iterator = activeExecutions.iterator(); iterator.hasNext();) {
                JobExecution jobExecution = iterator.next();
                try {
                    jobExecution = getJobExecution(jobExecution.getId());
                }
                catch (NoSuchJobExecutionException e) {
                    logger.error("Unexpected exception loading JobExecution", e);
                }
                if (!jobExecution.isRunning()) {
                    iterator.remove();
                }
            }
        **}**
    }

以上注释**表示更改。。

不确定为什么接受答案。这是不对的。next()的调用当然在hasNext()之后。这是正确的。缺少同步以防止此方法的两个或多个计划调用以及类中其他地方对集合的其他修改可能发生冲突。Collections.synchronizedList(新的ArrayList..)是不够的。

最新更新