我是Spring新手并对交易有疑问。
我知道每个http请求都有一个servlet线程,它有自己的堆栈。据我所知,所有局部变量和方法都驻留在堆栈上。因此,如果我有一个方法public void A();
,那么servlet线程A和线程B都在其堆栈中有一个函数的副本。
现在如果我使用@Transactional(propagation=Propagation.REQUIRED ,timeout=1,isolation=Isolation.READ_COMMITTED)
注释方法,那么我想知道以下几点:
每个线程A和线程B是否都有自己的堆栈并独立工作?
如果线程A正在更新某些内容而B正在读取某些东西,因为在不同的堆栈中,隔离是否有效?或者B将读取数据而不包含有关线程A?
我想通过图表了解这一点,以便我能理解端到端的工作原理是什么?
答案 0 :(得分:1)
线程B只会在线程A的事务完成后看到线程A完成的数据库修改。
线程B仍然可以从数据库中读取内容,然后线程A更新数据库中的内容并提交,然后线程B覆盖A写的线程。
为避免这种情况,您需要在数据库上使用某种类型的锁定,或使用更严格的隔离级别(但这会带来高性能损失,因为数据库通常需要执行大量锁定才能实现更严格的隔离级别)
答案 1 :(得分:1)
每个线程都有自己的堆栈,你就在这里。但他们没有方法的副本。方法只是一系列操作。但是他们在这个方法中有变量的副本(局部变量)。
谈到隔离级别,实际上它与线程和堆栈无关。它指的是数据库隔离级别概念。 Spring会要求您的数据库将该级别用于数据库事务。 PostreSQL有一个很好的doc page解释事务隔离。
所以在这里询问线程如何看到彼此并不完全正确,因为从数据的角度来看,他们看到了从数据库中获得的内容。并且数据库相应地返回当前事务隔离级别的数据。每个线程都会启动自己的事务,即它创建一个与数据库的新连接,并告诉它开始一个新的事务。
要了解幕后发生的事情,这里有一个例子。假设你有这样的方法:
@Transactional
public Person getPerson(int id) {
Person person = em.find(Person.class, id);
return person;
}
以下是每一行Spring引擎盖下的内容:
@Transactional
public Person getPerson(int id) {
// SQL sent to the database:
// Begin transaction
Person person = em.find(Person.class, id);
// SQL sent to the database:
// select p from person p where p.id = id
// the data from the database then gets converted to Java Person class
return person;
}
// after end of the method Spring automatically commits the transaction (not always, it depends on the `propagation` setting)
// SQL sent to the database:
// commit
请阅读PostgreSQL文档,深入解释事务隔离。 Java线程只接收与之相关的数据。
答案 2 :(得分:0)
Brifely:
现在为什么这样?在servlet环境中,你有一个或多个servlet和类级别的所有数据,因此在doHttp方法之外共享相同的数据,servlet不是线程安全的。
但当然,方法中的数据是线程安全的,因为每个方法调用都是一个调用函数,并且有一个用于提供方法请求的线程,并且它有自己的堆栈。
但是如果你有两个并发的方法调用,并且由于这个原因在两个独立的线程中,事务将是不一样的,因为thrad本地不一样并且不共享相同的db的immage它是自然的如果你考虑JPA,persistanceContext是存储在threadlocal中的第一级缓存。我的建议是使用更高级别的事务隔离来缓解可能的问题
我希望它可以帮到你