2017-04-30 47 views
0

需要使用多线程在数据库中插入数据,但即使单个线程未能提交,所有事务也必须回滚。试图通过以下方法解决此问题。处理多线程环境中的事务

在线程之间共享连接对象,并使用join()等待子线程完成,但是这看起来像坏设计,因为我在线程间共享连接对象。

有人可以建议更好的设计来解决这个问题(不知道我应该去分布式txn经理)吗?

+0

在父线程中使用CountDownLatch https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html。 还可以使用savePoint和rollback来控制回滚并监视事务进度https://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html – Saurabh

+0

savePoint和rollback在连接级别上工作,如果我使用多重连接,这只会回滚特定的txn,而不是所有正在其他线程中执行的txn。 – RE350

+0

我明白了,那就要求使用一些事务管理器。如果你使用它作为应用程序服务器,那么JBoss提供了一个,但是它也将你的应用程序绑定到JBoss。我不记得它是正确的,但Spring也有一些TM产品。 – Saurabh

回答

2

我建议将来自多个线程的所有SQL操作排列在某些中间数据结构中,然后从单个线程放入数据库。可以使用线程安全的中间结构,如ConcurrentHashMap,ConcurrentLinkedQueue,或者您可以在使用它时进行同步。

这样你甚至不需要事先启动交易。挂起的数据可能不太安全,但我认为它们在数据库中并不安全,而事务尚未提交。

当然,这只有在您没有select语句从同一事务中选择未提交的事务数据时才能使用。以这种或那种方式摆脱这种查询可能需要重新设计。

使用CountDownLatch来检测所有数据何时准备就绪,并且数据库写入线程应该开始其动作。如果从未发生,请使用reactor模式作为数据库写入线程。

1

这里是我最新的想法与可能的实现步骤:

  • 你父处理线程将(1)创建新的线程做并行数据库插入,(2)创建一个CountDownLatch对象(这将持有你的父线程,直到所有正在做数据库插入的子线程完成)(3.)创建一个自动提交模式为FALSE的数据库连接对象。
    • 假设你正在产卵6个线程做并行数据库插入,那么你将创建CountDownLatch对象这样CountDownLatch countDownLatch = new CountDownLatch(6),然后产卵的并行线程,做countDownLatch.await()
  • 您的并行线程将开始插入到数据库,但关键是他们每个人都在使用由父线程提供的自动提交FALSE模式的数据库连接对象,所以基本上没有子线程会执行数据库提交。
    • 一旦完成每个子线程,它们将执行countDownLatch.countDown();来减少锁存计数器。
    • 请注意,您需要提供countDownLatch以及db连接对象到每个线程,我相信你会知道如何。
  • 一旦锁存计数器为0,你的父母线程执行将重新开始(直到锁存计数器不为0,countDownLatch.await()将持有的线程),然后(1)你可以决定是否根据提交或不从每个线程结果(2.)关闭连接对象。现在,Runnable不会返回任何东西,所以最好使用Callable,以便每个线程都可以通知其状态。

如果你使用的是Spring,那么它可以通过它的事务特性来简化你的工作,但这会变成不同的故事。


现在,什么你在你的问题中提到几点 - 你提到的“即使单个线程未能提交,所有的交易必须回滚”,基本上如果你的任何数据库插入/接入失败,则你不想承诺任何事情,所以你的Callable将返回他们的执行状态,我不知道你可能意味着什么,但我认为如果你有点关于Callable那么你应该没问题。另外,你提到了“,但是这看起来像坏的设计,因为我在线程间共享连接对象。”“,你将需要共享数据库连接对象,因为一旦事务被提交,你就不能回滚,所以你不想回滚要共享连接对象,那么可能需要有一组SQL语句才能撤销先前数据库访问及其提交所完成的工作。