我们的要求是同时编写多个文件。我们正在使用spring批处理来编写文件,我们正在从不同的线程中进行春季批处理。每个线程都有自己的应用程序上下文。因此,我们可以确保单线程bean不会在多个线程中共享。以下是我的代码段。
Spring batch config。
<bean id="reportDataReader" class="com.test.ist.batch2.rrm.batch.readers.RRMItmeReader"
scope="step">
<property name="verifyCursorPosition" value="false" />
<property name="dataSource" ref="dataSource" />
<property name="sql" value="#{jobParameters['sqlquery']}" />
<property name="rowMapper" ref="valueMapper" />
<property name="fetchSize" value="5000" />
</bean>
<bean id="valueMapper" class="com.test.ist.batch2.rrm.batch.mappers.DBValueMapper" scope="step"></bean>
<bean id="velocityFileWritter"
class="com.test.ist.batch2.rrm.batch.writers.RRMVelocityFileWriter"
scope="step">
</bean>
<bean id="velocityEngine"
class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
<property name="velocityProperties">
<value>
resource.loader = class
class.resource.loader.class = org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
class.resource.loader.cache = true
class.resource.loader.modificationCheckInterval = 0
</value>
</property>
</bean>
<batch:job id="rrmReportGenJob">
<batch:step id="rrmReportGenStep">
<batch:tasklet>
<batch:chunk reader="reportDataReader" writer="velocityFileWritter"
commit-interval="${reportData.reader.commit-interval}">
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
我们如何调用弹簧批。
ThreadPoolExecutor tpe = new ThreadPoolExecutor(10,10,1000000,TimeUnit.MILLISECONDS,new LinkedBlockingQueue()); PetReportGenerator rrg = new PetReportGenerator(null); ThreadTest tt = new ThreadTest(new PetReportGenerator(null),&#34; 161&#34;); ThreadTest tt2 = new ThreadTest(new PetReportGenerator(null),&#34; 162&#34;); ThreadTest tt3 = new ThreadTest(new PetReportGenerator(null),&#34; 163&#34;); ThreadTest tt4 = new ThreadTest(new PetReportGenerator(null),&#34; 165&#34;); tpe.execute(TT); tpe.execute(TT2); tpe.execute(TT3); tpe.execute(TT4);
在PetReportGenerator的构造函数中,我们正在初始化bean配置。 以下是代码段
私有ApplicationContext appContext;
public PetReportGenerator(ApplicationContext reportContext){
if(null == reportContext){
//if(null == appContext){
appContext=new ClassPathXmlApplicationContext("spring-batch-jobs.xml");
//}
}else{
setAppContext(reportContext);;
}
}
以下是我们如何调用弹簧批次的代码摘录
Job jobToExecute =(Job)SpringUtils.getBean(jobName); JobParametersBuilder paramsBuilder = new JobParametersBuilder(); //默认情况下添加数据时间。这将有助于使用相同的参数再次完成相同的工作 paramsBuilder.addLong(&#34; JOB_TIME&#34;,System.currentTimeMillis()); 如果(!jobParams.isEmpty()){ //验证输入字段。 String sqlToUse = validator.validateInput(jobParams); for(Map.Entry entry:jobParams.entrySet()){
paramsBuilder.addString(entry.getKey(), entry.getValue());
}
}else{
throw new ReportGenerationException("Job input parameter is Empty");
}
jobexe=jobLauncher.run(jobToExecute, paramsBuilder.toJobParameters());
如果它在单个线程中运行,它运行正常。 当它被多个线程调用时,我们将收到错误
09:09:26,742 ERROR pool-1-thread-3 job.AbstractJob:329 - 遇到执行作业的致命错误 显示java.lang.NullPointerException 在org.springframework.batch.core.repository.dao.MapJobExecutionDao.synchronizeStatus(MapJobExecutionDao.java:158) 在org.springframework.batch.core.repository.support.SimpleJobRepository.update(SimpleJobRepository.java:161) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) 在org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317) 在org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) 在org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.transaction.interceptor.TransactionInterceptor $ 1.proceedWithInvocation(TransactionInterceptor.java:98) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) 在org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 在org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) 在com.sun.proxy。$ Proxy14.update(未知来源) 在org.springframework.batch.core.job.AbstractJob.updateStatus(AbstractJob.java:416) 在org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:299) 在org.springframework.batch.core.launch.support.SimpleJobLauncher $ 1.run(SimpleJobLauncher.java:135) 在org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) 在org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:128)
任何人都可以帮我理解可能出错的地方吗?
答案 0 :(得分:0)
你确定MapJobExecutionDao在所有方面都是线程安全的吗?我看到,在MapJobExecutionDao中使用了ConcurrentMap,但我不确定这是否足够。我曾经遇到过从不同线程访问的Map中获取NullPointer的问题。问题在于,一个线程导致了重新散列,当第二个线程在那一刻确实访问了地图时,它收到了一个空指针。
您确定,您的识别工作参数的组合是唯一的吗?我看,你在System.currentTimeMillis()中添加了一个参数Job_Time,但是你知道吗,如果它真的在一个唯一的时间戳中解析了吗?
您是否尝试使用基于表格的JobExecutionDao等版本?
答案 1 :(得分:0)
用于生产用途的MapJobRepository
NOT 。它是 NOT 线程安全。如果您需要内存作业存储库的性能(失去可重启性等),请使用内存数据库,如HSQLDB。
除此之外,如果您使用线程安全组件,则没有理由不能使用多个线程启动多个作业实例。