2

我正在尝试为使用 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();
}

最初的问题是,如果 2 个不同的线程大致同时到达同一个代码块,它们都会尝试并获得相同版本的对象,所以第二个线程总是会失败。由于有多个负载平衡的服务器,因此该问题的真正解决方案是使用分布式锁定系统来确保版本保持同步并且事务不会相互影响。但是,由于用户已经发现这是一个问题,并且该问题的长期解决方案需要时间来开发,因此我做出了执行决定,通过检查对象是否在事务之前已更新来添加临时黑客开始。我创建了第二个会话来执行此版本检查。如果有更新,我会使用第二个会话来填充对象' s 字段然后保存它。所以新代码看起来有点像这样:

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会将此报告为死锁,因为只有一个事务。

对此的任何想法都非常感谢!

4

1 回答 1

0

最初的问题是,如果 2 个不同的线程大致同时到达同一个代码块,它们都会尝试并获得相同版本的对象,所以第二个线程总是会失败。

您的第二个解决方案仍然有同样的问题。你正在这样做,

  1. 获取最新对象
  2. 如果版本不同,则更新字段
  3. 提交更新的对象

当您执行第 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获取更新锁来获取对象。您应该了解使用悲观锁定而不是乐观锁定的优缺点。

于 2012-09-26T08:20:23.427 回答