Spring事务与自定义多线程陷阱

场景:Spring+Ibatis环境,使用spring aop事务(配置到service层),在一个service方法中,自定义了一个多线程,结果事务不起作用了,不用线程,,则事务有效。

原因:Spring的事务是通过ThreadLocal来保证线程安全的,事务和当前线程绑定,所以自己开了多线程自然会让事务失效。

Spring的事务管理器是通过ThreadLocal来保存每个线程的副本,从而实现线程安全的,再结合IoC和Aop实现高级声明式事务的功能,所以Spring的事务天然地和线程有着千丝万缕的联系。只能维护web应用的多线程,不支持多线程里的多线程。

其他方案:修改代码架构,把逻辑处理部分抽出来,放在另外一个service中,然后通过xxx.service的方法去调用(在事务范围外做的线程操作),这样就有了事务。

应用场景:对历史数据进行迭代处理,处理完成一条就添加到数据库,不成功则抛出异常(如果不使用多线程则可以做到一批数据要么全部成功,有一个失败就全部回滚)。

代码片段如下:

代码一ReclaimMatchSubscriptionServiceImpl、

private void saveHistoryMatches(final MatchedInfoParams params, final CommonTaskName taskName, final List<Integer> matchIds) {new Thread() {@Overridepublic void run() {MatchedInfoParams clonedParams = params.clone();for (final Integer matchId : matchIds) {try {clonedParams.setMatchId(matchId);saveWorkingTask(clonedParams, taskName, TaskPriority.LOW);} catch (Exception e) {logger.error("Save history matches to working task failed: matchedId=" + matchId + ", companyId=" + params.getCompanyId() + ", taskName=" + taskName+ ", matchType=" + params.getMatchType(), e);}}}}.start();}public void saveWorkingTask(T params, CommonTaskName taskName, TaskPriority priority) {Assert.notNull(params, "CommonTaskParams must not be null");Assert.notNull(taskName, "CommonTaskName must not be null");Assert.notNull(priority, "TaskPriority must not be null");if (isServiceToDeal(taskName)) {String uniqueKey = new MD5().MD5(uniqueKey(params));WorkingCommonTask oldTask = commonTaskService.getWorkingTaskByNameAndKey(taskName, uniqueKey);if (isAddToWorkingTask(oldTask)) {WorkingCommonTask task = new WorkingCommonTask();task.setCompanyId(params.getCompanyId());task.setTaskName(taskName.getCode());task.setUniqueKey(uniqueKey);task.setRetrievalField(retrievalField(params));//task.setExtraInfo("");task.setRetryCount(0);task.setTaskPriority(priority.getCode());task.setTaskStatus(CommonTaskStatus.CREATED.getCode());task.setTimeout(0);commonTaskService.saveWorkingTask(task, getBiz(params));}}}

代码二CommonTaskServiceImpl、

public int saveWorkingTask(WorkingCommonTask workingTask, String bizData) {Assert.notNull(workingTask, "workingTask must not be null");Assert.notNull(bizData, "bizData must not be null");if (workingTask.getTimeout() <= 0) {workingTask.setTimeout(timeout);}int taskId = commonTaskDao.saveWorkingTask(workingTask);CommonTaskExtraInfo taskExtraInfo = new CommonTaskExtraInfo();taskExtraInfo.setTaskId(taskId);taskExtraInfo.setBizData(bizData);commonTaskDao.saveTaskExtraInfo(taskExtraInfo);commonTaskDao.saveTaskLog(new CommonTaskLog(taskId, workingTask.getTaskStatus(), workingTask.getExtraInfo()));return taskId;}

想起那座山,那个城,那些人……

Spring事务与自定义多线程陷阱

相关文章:

你感兴趣的文章:

标签云: