跨多线程解决方案的单一事务

Dov*_*vmo 10 java concurrency multithreading transactions

据我了解,所有事务都是线程绑定的(即上下文存储在ThreadLocal中).例如,如果:

  1. 我在事务父方法中启动事务
  2. 使数据库在异步调用中插入#1
  3. 使数据库在另一个异步调用中插入#2

然后,即使它们共享相同的"事务性"父级,也会产生两个不同的事务(每个插入一个).

例如,假设我执行两次插入(并使用非常简单的示例,即为了简洁起见,不使用执行程序或可完成的未来等):

@Transactional
public void addInTransactionWithAnnotation() {
    addNewRow();
    addNewRow();
}
Run Code Online (Sandbox Code Playgroud)

将根据需要执行两个插入作为同一事务的一部分.

但是,如果我想并行化这些插入的性能:

@Transactional
public void addInTransactionWithAnnotation() {
    new Thread(this::addNewRow).start();
    new Thread(this::addNewRow).start();
}
Run Code Online (Sandbox Code Playgroud)

然后,这些生成的线程中的每一个都不会参与事务,因为事务是线程绑定的.

关键问题:有没有办法将事务安全地传播到子线程?

我想到的解决这个问题的唯一解决方案:

  1. 使用JTA或某些XA管理器,根据定义应该能够执行此操作.但是,我理想情况下不希望将XA用于我的解决方案,因为它的开销很大
  2. 将我想要执行的所有事务工作(在上面的示例中,addNewRow()函数)传递给单个线程,并以多线程方式执行所有先前的工作.
  3. 找出在Transaction状态上利用InheritableThreadLocal并将其传播到子线程的某种方法.我不知道该怎么做.

有没有更多可能的解决方案?即使它的味道有点像解决方法(比如上面的解决方案)?

gpe*_*che 2

首先,澄清一下:如果您想加速同一类型的多个插入,正如您的示例所示,通过在同一线程中发出插入并使用某种类型的批量插入,您可能会获得最佳性能。根据您的 DBMS,有多种可用技术,请查看:

至于您的实际问题,我个人会尝试将所有工作传输到工作线程。这是最简单的选项,因为您不需要搞乱ThreadLocals 或事务登记/除名。此外,一旦您将工作单元放在同一线程中,如果您足够聪明,您也许能够应用上面的批处理技术以获得更好的性能。

最后,将工作管道传输到工作线程并不意味着您必须有一个工作线程,您可以拥有一个工作线程池并实现一些并行性(如果它确实对您的应用程序有益)。从生产者/消费者的角度思考。