我正在尝试实现一个多线程解决方案,这样我就可以并行化我的业务逻辑,包括读取和写入数据库。
技术栈:Spring 4.0.2、Hibernate 4.3.8
这是一些要讨论的代码:
配置
@Configuration
public class PartitionersConfig {
@Bean
public ForkJoinPoolFactoryBean forkJoinPoolFactoryBean() {
final ForkJoinPoolFactoryBean poolFactory = new ForkJoinPoolFactoryBean();
return poolFactory;
}
}
服务
@Service
@Transactional
public class MyService {
@Autowired
private OtherService otherService;
@Autowired
private ForkJoinPool forkJoinPool;
@Autowired
private MyDao myDao;
public void performPartitionedActionOnIds() {
final ArrayList<UUID> ids = otherService.getIds();
MyIdPartitioner task = new MyIdsPartitioner(ids, myDao, 0, ids.size() - 1);
forkJoinPool.invoke(task);
}
}
存储库/DAO
@Repository
@Transactional(propagation = Propagation.MANDATORY)
public class IdsDao {
public MyData getData(List<UUID> list) {
// ...
}
}
递归动作
public class MyIdsPartitioner extends RecursiveAction {
private static final long serialVersionUID = 1L;
private static final int THRESHOLD = 100;
private ArrayList<UUID> ids;
private int fromIndex;
private int toIndex;
private MyDao myDao;
public MyIdsPartitioner(ArrayList<UUID> ids, MyDao myDao, int fromIndex, int toIndex) {
this.ids = ids;
this.fromIndex = fromIndex;
this.toIndex = toIndex;
this.myDao = myDao;
}
@Override
protected void compute() {
if (computationSetIsSamllEnough()) {
computeDirectly();
} else {
int leftToIndex = fromIndex + (toIndex - fromIndex) / 2;
MyIdsPartitioner leftPartitioner = new MyIdsPartitioner(ids, myDao, fromIndex, leftToIndex);
MyIdsPartitioner rightPartitioner = new MyIdsPartitioner(ids, myDao, leftToIndex + 1, toIndex);
invokeAll(leftPartitioner, rightPartitioner);
}
}
private boolean computationSetIsSamllEnough() {
return (toIndex - fromIndex) < THRESHOLD;
}
private void computeDirectly() {
final List<UUID> subList = ids.subList(fromIndex, toIndex);
final MyData myData = myDao.getData(sublist);
modifyTheData(myData);
}
private void modifyTheData(MyData myData) {
// ...
// write to DB
}
}
执行此操作后,我得到:
没有找到标记为传播“强制”的交易的现有交易
我知道这是完全正常的,因为事务不会通过不同的线程传播。因此,一种解决方案是在另一个类似问题中提出的每个线程中手动创建一个事务。但这对我来说还不够令人满意,所以我一直在寻找。
在 Spring 的论坛中,我发现了关于该主题的讨论。有一段我觉得很有趣:
“我可以想象一个人可以手动将事务上下文传播到另一个线程,但我认为你不应该真正尝试它。事务绑定到单个线程是有原因的 - 基本的底层资源 - jdbc 连接 - 不是线程安全的。使用多线程中的单个连接会破坏基本的 jdbc 请求/响应合同,并且它是否可以在更多琐碎的示例中工作,这将是一个小问题。”
于是第一个问题出现了:并行化对数据库的读/写是否值得,这真的会损害数据库的一致性吗?
如果上面的引用不正确,我怀疑,有没有办法实现以下目标:
由 Spring 管理的 MyIdPartitioner - 使用 @Scope("prototype") - 并将递归调用所需的参数传递给它并以这种方式将事务管理留给 Spring?