Java 什么时候在GAE中抛出ConcurrentModificationException?

Java 什么时候在GAE中抛出ConcurrentModificationException?,java,transactions,google-cloud-datastore,Java,Transactions,Google Cloud Datastore,我正在阅读官方GAE,无法理解何时抛出ConcurrentModificationException 请看我在这里复制粘贴的一个示例: int retries = 3; while (true) { Transaction txn = datastore.beginTransaction(); try { Key boardKey = KeyFactory.createKey("MessageBoard", boardName); Entity m

我正在阅读官方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

您似乎在做他们建议您不应该做的事情:

警告!上面的示例描述了仅为简单起见对计数器进行事务性递增。如果你的应用程序有频繁更新的计数器,你不应该以事务方式增加它们,甚至不应该在单个实体内增加它们。使用计数器的最佳实践是使用称为计数器分片的技术

也许上述警告并不适用,但它似乎暗示了您所看到的问题:

这需要一个事务,因为在该代码获取对象之后,但在保存修改后的对象之前,其他用户可能会更新该值。在没有事务的情况下,用户的请求在另一个用户更新之前使用count的值,保存覆盖新值。在事务中,应用程序被告知其他用户的更新。如果在事务期间更新了实体,则事务将失败,出现
ConcurrentModificationException
。应用程序可以重复事务以使用新数据

换句话说:在您使用事务更新同一实体的同时,似乎有人在修改您的实体而不使用事务

注意:在极少数情况下,即使事务返回超时或内部错误异常,事务也会完全提交。因此,最好尽可能使事务幂等


一个合理的警告:我不熟悉这个库,但上面的引文取自显示示例事务的文档(这似乎与您在原始问题中发布的内容相同)。

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

我对GAE中的事务如何工作有一个错误的概念。我曾设想,在事务提交之前,启动事务将锁定对数据存储的任何并发更新。这将是一场性能噩梦,因为所有更新都会阻止此事务,我很高兴事实并非如此

相反,第一次更新获胜,如果在后续更新中检测到冲突,则会引发异常


一开始这让我很惊讶,因为这意味着许多事务都需要重试逻辑。但它似乎与“可序列化隔离”级别的PostgreSQL语义相似,不过在PostgreSQL中,您也可以选择锁定单个行和列。

该警告的原因完全不同:性能。在交易的上下文中,这是一个非常有效的例子。HRJ,在警告后的段落中,它还指出,如果实体在交易过程中更新,则交易可能会因
ConcurrentModificationException
而失败。我想这就是本案发生的情况。谢谢你试着回答这个问题。我已经知道文档中的内容了。我需要一个没有假设的答案。@HRJ,在看不到/不知道还有什么可以改变你的实体的情况下,我想说我们所能做的就是假设。不管怎样,祝你好运。这和我提供的引文的第二段有什么不同?Lirik,它和你的引文没什么不同。但是你下面的总结和我的不同,答案中有一些假设和不相关的引用。因此,我在澄清自己困惑的同时做出了新的回答。感谢您参与这项任务。