是否可以在另一个线程中使用事务?
与在主题A 中创建的交易一样,然后在同一交易中的主题B 中执行某些逻辑?
我有两个队列和单独的执行器来处理某些实体类型的填充。
但是,批处理作业管理两个人口并等待每个人完成。创建两个事务将是不必要的。如果一个失败,理想情况下我希望回滚所有数据,这样就可以将它们作为一个事务运行,并且它可以提供更好的性能。
那么,是否可以创建一个事务,将其传递给另一个线程,在第一个线程的边界内执行一些事情?
我正在使用Spring和Hibernate,目前正在使用
123
456
789
创建交易,根本不使用注释,也没有计划。
答案 0 :(得分:5)
Spring无法实现。所有基于事务的代码最终都以TransactionSynchronizationManager
结尾,其中包含ThreadLocal
个,并且无法将这些值从一个线程复制到另一个线程。
如果您希望能够执行此操作,可以使用Spring获取DataSource
,但您必须创建自己的连接并手动创建Hibernate会话。 Spring的PlatformTransactionManager
是不可能的。
[编辑] 在你的情况下有多个线程有什么意义?如果你想并行化工作,那么正确的方法是有N个线程准备应该插入数据库的数据,然后是一个创建1个事务然后完成所有工作的线程。
是的,这意味着您需要在内存中保留大量数据。
如果你真的不想这样做,那么下一个解决方案就是让工作表中每个线程放置他们的结果。当两个线程完成时,您启动另一个线程来锁定工作表并运行一些SQL查询以将数据复制到正确的位置。
始终牢记数据库连接,SQL和线程不会混合。数据库是全局状态。如果你同时从几个地方改变全局状态,你将永远面临各种奇怪的问题。尽量避免这种情况。将工作分成许多小的,独立的任务(即,当每个任务都有自己的事务时,它们工作得很好)。如果你不能这样做,你需要重新考虑你的设计,直到你可以或你不能使用线程。
答案 1 :(得分:3)
在Spring中,实际上可以通过一个简单的技巧将事务上下文从一个线程转移到另一个线程,如下所示:
org.springframework.orm.hibernate4.SessionHolder
应该足够了,因为它还保持与正在进行的事务的链接。我们需要关注细微的细节,但以下代码应该完成工作:
org.springframework.transaction.support.TransactionSynchronizationManager.bindResource(Object, Object)
希望这有帮助。
答案 2 :(得分:2)
我决定提交自己的答案,允许在所有环境和框架中以不同的方式实现这一点,方法是让其他线程将工作委托给原始线程。
示例:
public static void main(String[] args) throws Exception {
System.out.println(Thread.currentThread().getId() + ": (1)");
JoinLock lock = new JoinLock();
new Thread(() -> {
lock.delegate(() -> {
System.out.println(Thread.currentThread().getId() + ": (a) "); // Will execute in the originating thread
});
}).start();
lock.await();
System.out.println(Thread.currentThread().getId() + ": (2)");
}
输出:
1: (1)
1: (a)
1: (2)
另一个使用传递的runnable和更多细节的例子:
public static void main(String[] args) throws Exception {
System.out.println(Thread.currentThread().getId() + ": (1)");
new JoinLock().await((lock) -> {
new Thread(() -> {
// Should execute as another thread
System.out.println(Thread.currentThread().getId() + ": (a) ");
// Will execute in the originating thread
lock.delegate(() -> {
System.out.println(Thread.currentThread().getId() + ": (b) ");
sleep(2000);
System.out.println(Thread.currentThread().getId() + ": (c) ");
});
// This should execute first when the delegate has finished executed, so after 1 : (c) but since it's a separate thread,
// ofcourse the originating thread might also execute prior, which happens because of our sleep here
sleep(2000);
System.out.println(Thread.currentThread().getId() + ": (d) ");
}).start();
});
System.out.println(Thread.currentThread().getId() + ": (2)");
}
private static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
输出:
1: (1)
13: (a)
1: (b)
1: (c)
1: (2)
13: (d)
使用执行程序会是这样的:
new JoinLock().await((lock) -> {
Executors.newFixedThreadPool(1).submit(() -> {
lock.delegate(runnable);
});
});
示例中使用的锁可以在这里找到:
GitHub: Opensource repository。请查看:JoinLock.java,它还使用:SimpleLock.java。