热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Spring事务管理与自定义多线程开发中的潜在风险分析

在Spring与Ibatis集成的环境中,通过SpringAOP配置事务管理至服务层。当在一个服务方法中引入自定义多线程时,发现事务管理功能失效。若不使用多线程,事务管理则能正常工作。本文深入分析了这一现象背后的潜在风险,并探讨了可能的解决方案,以确保事务一致性和线程安全。

        场景: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 matchIds) {
new Thread() {
@Override
public 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.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;
}

另外,对于第一段代码事务失效,还有另外一种解释:

public class A {
@annotation
public B () {

}

public C () {
B();
}

}

当一个类中,方法B上面有注解(譬如:事务注解),方法B被方法C调用,当其他方法调用方法C的时候方法B上面的注解是无效的!


推荐阅读
author-avatar
讲述华哥传奇的生活_616
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有