我正在尝试使用hibernate为java服务器中的竞争条件提供临时解决方法。代码最初看起来像这样:
s = sessionFactory.openSession();
Object o1 = dao.getMostRecentVersionOfObject(key, s);
if (o1.performSomeTimeConsumingTask() == Looks.Ok) {
Transaction t = s.beginTransaction();
dao.update(o1, s);
t.commit();
}
最初的问题是,如果两个不同的线程几乎同时到达相同的代码块,它们都会尝试获得相同版本的对象,因此第二个将始终失败。由于存在多个负载均衡的服务器,因此该问题的真正解决方案是使用分布式锁定系统来确保版本保持同步并且事务不会彼此依赖于脚本。但是,由于用户已经发现这是一个问题,并且这个问题的长期解决方案需要时间来开发,我做出了执行决定,通过检查对象是否在事务发生之前更新来添加临时黑客开始。我创建了第二个会话来执行此版本检查。如果有更新,我使用第二个会话来填充对象的字段然后保存它。所以新代码看起来有点像这样:
Session s = sessionFactory.openSession();
Session transactionSession = s;
Object o1 = dao.getMostRecentVersionOfObject(key, s);
int version = o1.getVersion();
if (o1.performSomeTimeConsumingTask() == Looks.Ok) {
Session newerSession = sessionFactory.openSession();
Object newerObject = dao.getMostRecentVersionOfObject(key, newerSession);
if (newerObject.getVersion() > version) {
// update fields...
transactionSession = newSession;
}
}
Transaction t = transactionSession.beginTransaction();
dao.update(o1, transactionSession);
t.commit();
此代码适用于多种环境,但由于报告的死锁而在最重要的环境中失败。当没有对该方法的第二个并发请求时,会发生这种情况 - 第二个会话被创建,检查版本,然后在第一个会话执行提交事务时被忽略。我怀疑这个问题要么是环境问题(但我不明白为什么会出现这种情况),或者说hibernate不喜欢使用第二个会话,但这只是一个有根据的猜测。我特别感到困惑的是,为什么hibernate会将此报告为死锁,因为只有一个事务。
对此的任何想法都非常感谢!
答案 0 :(得分:0)
最初的问题是,如果两个不同的线程几乎同时到达同一个代码块,它们都会尝试获取相同版本的对象,因此第二个将始终失败。
您的第二个解决方案仍然存在同样的问题。你这样做,
当您执行第2步时,其他线程会更新该对象。
如果您想防止这种情况发生,请同步方法(最不喜欢)或执行以下操作,
Session s = sessionFactory.openSession();
Object o1 =s.get('o1s class',key,new LockOptions(LockMode.PESSIMISTIC_WRITE));
//Do whatever you want with o1
//Update o1
//commit
我们的想法是通过从db.See doc获取更新锁来获取对象。你应该意识到使用悲观锁定而不是乐观锁定的利弊。