Joh*_*ohn 5 spring hibernate jpa transactions synchronized
我的同事和我有一个Web应用程序,它在MyEclipse里面的Tomcat上使用Spring 3.0.0和JPA(hibernate 3.5.0-Beta2).其中一个数据结构是树.为了好玩,我们尝试使用JMeter对"插入节点"操作进行压力测试,并发现了并发问题.Hibernate报告在发出如下警告之后发现具有相同私钥的两个实体:
WARN [org.hibernate.engine.loading.LoadContexts] fail-safe cleanup (collections) : ...
Run Code Online (Sandbox Code Playgroud)
如果多个线程同时调用insert()方法,很容易看出这些问题可能会发生.
我的servlet A调用服务层对象B.execute(),然后调用较低层对象C.insert().(真正的代码太大了,不能发布,所以这有点删节.)
Servlet A:
public void doPost(Request request, Response response) {
...
b.execute(parameters);
...
}
Run Code Online (Sandbox Code Playgroud)
服务B:
@Transactional //** Delete this line to fix the problem.
public synchronized void execute(parameters) {
log("b.execute() starting. This="+this);
...
c.insert(params);
...
log("b.execute() finishing. This="+this);
}
Run Code Online (Sandbox Code Playgroud)
子服务C:
@Transactional
public void insert(params) {
...
// data structure manipulation operations that should not be
// simultaneous with any other manipulation operations called by B.
...
}
Run Code Online (Sandbox Code Playgroud)
我所有的状态更改调用都通过B,所以我决定创建B.execute()synchronized.它已经是@Transactional,但它实际上是需要同步的业务逻辑,而不仅仅是持久性,所以这似乎是合理的.
我的C.insert()方法也是@Transactional.但由于Spring中的默认事务传播似乎是必需的,因此我认为没有为C.insert()创建任何新事务.
所有组分A,B和C都是弹簧豆,因此是单体.如果确实只有一个B对象,那么我得出结论,一次执行b.execute()不应该有多个威胁.当负载很轻时,只使用一个线程,情况就是这样.但是在负载下,其他线程也会被涉及,我看到几个线程在第一个打印"完成"之前打印"开始".这似乎违反synchronized了该方法的性质.
我决定this在日志消息中打印以确认是否只有一个B对象.所有日志消息都显示相同的对象ID.
在经历了令人沮丧的调查后,我发现删除@Transactionalfor B.execute()解决了这个问题.随着那条线的消失,我可以拥有很多线程,但我总是会在下一个"开始"之前看到一个"开始",然后是"完成"(我的数据结构保持不变).不知何故,synchronized只有当@Transactional它不存在时才会起作用.但我不明白为什么.有人可以帮忙吗?关于如何进一步研究这个问题的任何提示?
在堆栈跟踪中,我可以看到在A.doPost()和B.execute()之间生成了一个aop/cglib代理 - 以及B.execute()和C.insert()之间.我想知道代理的构造是否会破坏synchronized行为.
问题是@Transactional封装了synchronized方法.Spring使用AOP做到这一点.执行是这样的:
步骤1.和3.可以由多个线程同时执行.因此,您可以获得多次交易.
您唯一的解决方案是将调用与方法本身同步.
正如您所说,同步关键字要求涉及的对象始终相同。我自己没有观察到上述行为,但你的怀疑可能是正确的。
您是否尝试过从 doPost 方法中注销 b ?如果每次都不同,那么 AOP/cglib 代理就会发挥一些 Spring 魔力。
无论如何,我不会依赖syncronized关键字,而是使用java.util.concurrent.locks中的ReentrantLock之类的东西来确保同步行为,因为无论可能有多个cglib代理,您的b对象总是相同的。
| 归档时间: |
|
| 查看次数: |
3698 次 |
| 最近记录: |