我有一个控制器,每个请求并行执行几个作业,如下所示:
@Controller
@RequestMapping("/uploadjob")
public class UploadJobController extends BaseController {
....
@RequestMapping(value = "/uploadjob", method = RequestMethod.POST)
public String runupload(@ModelAttribute("uploadParameters") UploadParameters uploadParameters,
BindingResult result, HttpServletRequest request){
...
for (Line i: lines){
try {
this.jobLauncher.run(this.uploadProjectDataJobNormal, jobParameters);
}
}
在某些时候,作业从文件中读取元素(在每个作业执行中不同)并复制到表中但删除了重复的元素。
因为作业是并行执行的,所以在插入元素之前的每个作业中,我们查询数据库中的现有元素以避免重复。我们希望使用:
来避免这种情况 List<String> existingElements
但是这个对象应该由所有作业共享,并且同时由所有作业更新,因此该对象需要是线程安全的。我在这里看到不同的问题,解决方案可以使用会话对象(HttpSession对象)来存储列表并仔细访问对象以避免在作业的步骤中出现竞争条件。
我说错了吗?是否有另一种方法可以实现相同的结果(不改变实际的工作流程)?
答案 0 :(得分:0)
尝试不同的解决方案后,我们就这样实现了。我们创建一个对象来存储要共享的对象:
public class JobCache {
private HashMap<TypeClass,Set<String>> ExistingNames;
private final UUID id;
...
id字段最终用于同步访问属性existingNames。我们使用下一个语句修改了不同作业之间的缓存:
synchronized (jobCache.getId()){
jobCache.addNames(names,TypeClass.TYPE1);
}
我们知道这不是完全线程安全的,但在我们的环境下(因为创建作业的方式)。
获取名称(即使不需要,只是为了确定,我们使用这个:
synchronized(jobCache.getId()){
this.names = jobCache.getExistingNames(TypeClass.TYPE1);
}
也许它不是理想和最优雅的解决方案,但它对我们有用。