我正在尝试实现一个多线程解决方案,以便并行化我的业务逻辑,包括读写数据库。
技术栈: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);
}
}
仓库 / 数据访问对象
@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
}
}
执行后我得到了以下结果:
我理解这是很正常的,因为事务不会在不同的线程中传播。因此,一种解决方案是在每个线程中手动创建事务如另一个类似问题所建议的那样。但是这对我来说还不够令人满意,所以我继续搜索。没有找到与传播“mandatory”的标记标记的事务对应的现有事务
在Spring论坛中,我发现了关于该主题的讨论。其中一个段落我认为非常有趣:
"我可以想象人们可以手动将事务上下文传播到另一个线程,但我认为你真的不应该尝试。事务绑定到单个线程是有原因的 - 基本的底层资源 - jdbc连接 - 不是线程安全的。在多个线程中使用一个连接将破坏基本的jdbc请求/响应协议,并且如果它能在更复杂的示例中工作,那将是一个小奇迹。"
所以第一个问题是:并行读写数据库值得吗?这样做会真正损害数据库的一致性吗?
如果上面的引用不是真的,我怀疑,是否有办法实现以下内容:MyIdPartitioner由Spring管理 - 使用@Scope("prototype") - 并将递归调用所需的参数传递给它,这样就可以将事务管理留给Spring?