GAE中什么时候会抛出ConcurrentModificationException?

6

我正在阅读官方 GAE 事务文档,但我无法理解何时会抛出ConcurrentModificationException异常。

请看下面的示例,我将其复制粘贴在此处:

int retries = 3;
while (true) {
    Transaction txn = datastore.beginTransaction();
    try {
        Key boardKey = KeyFactory.createKey("MessageBoard", boardName);
        Entity messageBoard = datastore.get(boardKey);

        long count = (Long) messageBoard.getProperty("count");
        ++count;
        messageBoard.setProperty("count", count);
        datastore.put(messageBoard);

        txn.commit();
        break;
    } catch (ConcurrentModificationException e) {
        if (retries == 0) {
            throw e;
        }
        // Allow retry to occur
        --retries;
    } finally {
        if (txn.isActive()) {
            txn.rollback();
        }
    }
}

现在,所有对数据存储区的写入(在本示例中)都在事务下完成。那么为什么会抛出“ConcurrentModificationException”呢?
如果没有包含在事务中的其他代码更新了被上述代码修改的同一实体,那么会发生吗?如果确保所有更新实体的代码始终包含在事务中,是否有保证不会收到“ConcurrentModificationException”呢?
2个回答

1

我在 GAE 的邮件列表中找到了答案。

我对 GAE 中事务是如何工作的有一种误解。我曾经想象过开始一个事务将会锁定数据存储中的任何并发更新,直到事务提交。那将会是一个性能噩梦,因为所有的更新都会在此事务上阻塞,我很高兴这不是这种情况。

相反,发生的情况是,第一个更新获胜,如果在后续更新中检测到冲突,则会抛出异常。

起初这让我感到惊讶,因为这意味着许多事务都需要重试逻辑。但它似乎类似于 PostgreSQL 中“可串行化隔离”级别的语义,尽管在 PostgreSQL 中你也有锁定单个行和列的选项。


那与我提供的引用的第二段有什么不同? - Kiril
Lirik,它与您包含的引用并没有太大区别。但是您在引用下面的摘要与我的不同,并且答案中有一些假设和无关的引用。因此,我在澄清自己的困惑时提供了一个新的答案。感谢您参与这个问题。 - HRJ

0

看起来你正在做他们建议不要做的事情:http://code.google.com/appengine/docs/java/datastore/transactions.html#Uses_for_Transactions

警告!上面的示例仅为简单起见而描述了事务性地递增计数器。如果您的应用程序具有经常更新的计数器,则不应在事务中递增它们,甚至不应在单个实体内递增它们。处理计数器的最佳实践是使用一种称为计数器分片的技术。

也许上述警告不适用于您,但其后的内容似乎暗示了您所看到的问题:

这需要一个事务,因为在代码获取对象后但在保存修改的对象之前,该值可能会被另一个用户更新。如果没有事务,用户请求使用先前其他用户更新的计数值,并且保存将覆盖新值。有了事务,应用程序将了解到其他用户的更新。如果在事务期间更新了实体,则事务将失败并返回ConcurrentModificationException。应用程序可以重复事务以使用新数据。
换句话说,似乎有人正在修改您的实体,而您正在使用事务更新相同的实体。
请注意:在极少数情况下,即使事务返回超时或内部错误异常,事务仍完全提交。因此,在可能的情况下最好使事务幂等。
公平警告:我不熟悉该库,但上述引用摘自显示示例事务的文档(似乎与您在原始问题中发布的内容相同)。

那个警告是出于完全不同的原因:性能。在事务的上下文中,这是一个完全有效的例子。 - HRJ
HRJ,在警告后的段落中还指出,如果在事务期间更新实体,则事务可能会失败并出现“ConcurrentModificationException”。我认为这就是此案例中发生的情况。 - Kiril
谢谢您尝试回答问题。我已经知道文档中的内容了。我需要一个没有假设的答案。 - HRJ
@HRJ,不知道还有什么其他的东西可能会修改你的实体,所以我只能说我们只能做出一些假设。无论如何,祝你好运。 - Kiril

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接