Java MySQL 防止竞争条件

Posted

技术标签:

【中文标题】Java MySQL 防止竞争条件【英文标题】:Java MySQL prevent race condition 【发布时间】:2016-03-21 22:01:27 【问题描述】:

我编写了一个 java 应用程序,它启动异步线程以从同一个数据库读取和更新值。每个线程都从连接池 (c3p0) 中获取连接。我必须防止出现竞争条件,因为我必须根据它们的当前值更新条目。因此,使用SELECT 语句读取数据,然后使用UPDATE 语句对其进行更新会导致竞争条件,因此它不是线程安全的。我已经找到了一些解决方案如何防止这种竞争条件,但我仍然有一些问题。

例如,我可以使用UPDATE ExampleTable SET ExampleValue = ExampleValue + '5' WHERE Id = '10' 来增加线程安全的值。我读到这是一个原子声明。所以我的第一个问题是:在 java 中执行 PreparedStatement 总是线程安全的吗?我认为是因为(如果 autoCommit 为真)每个执行的语句都是一个事务,并且事务是原子的,对吗?如果是的话,如果我用一个语句调用一个过程,或者如果我将多个查询放在一个用分号分隔的语句中,是否也是这种情况?

我还读到我可以将 autoCommit 设置为 false 并在提交之前执行多个语句,这也实现了线程安全,因为没有其他语句可以中断事务。对吗?

是否有任何进一步的解决方案来防止这种竞争条件?

【问题讨论】:

我认为this 回答了您的一些问题,您检查了吗?特别是:“给每个线程自己的连接”。我通过连接池以这种方式执行此操作,并且没有任何竞争条件(尽管我使用的是 PostgreSQL 而不是 mysql)。 我使用一个连接池,每个线程都有自己的连接池对象。但是当使用单独的语句进行读取和写入时,仍然可能出现竞争条件。 作为 EJP 的回答,使用SELECT FOR UPDATE,这样你只有一个语句。如果您想执行更多查询/更新或SELECT FOR UPDATE 不是一个选项,您需要将它们包装到一个事务中(当然每个线程仍然一个连接)。 @m0skit0 我不需要第二条语句来执行更新吗?例如:SELECT counter_field FROM child_codes FOR UPDATE;UPDATE child_codes SET counter_field = counter_field + 1; 至于你的最后一个问题:ACID 中的 i 应该回答这个问题。从链接中引用:“......事务的并发执行导致系统状态,如果事务是串行执行的......”。只要确保您使用正确的isolation level。 【参考方案1】:

在 java 中执行 PreparedStatement 是否总是线程安全的?

我不确定这意味着什么。您只能在单个线程中使用 PreparedStatement 以及其底层 Connection,因此不会出现问题。

我认为是因为(如果 autoCommit 为真)每个执行的语句都是一个事务,并且事务是原子的,对吗?如果是的话,如果我用一个语句调用一个过程,或者如果我将多个查询放在一个用分号分隔的语句中,是否也是这种情况?

我是这么想的。您真正要问的是PreparedStatement 是否是原子的, 是否跨线程或进程,除非您使用自动提交,否则答案是否定的。如果您使用事务,则事务是原子的。 [事实上,事务总是原子的,但在自动提交模式下,它与语句是共同扩展的。]

我还读到我可以将 autoCommit 设置为 false 并在提交之前执行多个语句,这也实现了线程安全,因为没有其他语句可以中断事务。对吗?

它实现了原子性。线程安全完全是另一回事。

您可能正在寻找非自动提交事务中的SELECT... FOR UPDATE。它锁定返回的行,以便在提交此事务之前,它们不能被另一个此类语句返回。或者像INSERT ... ON DUPLICATE KEY [IGNORE|UPDATE]这样的结构。

【讨论】:

多个语句呢? 你的意思是我根本不应该使用自动提交吗? autoCommit 不是默认设置吗?你能回答其他问题吗?执行一条语句是否总是线程安全的? @stonar96 我链接的答案已经回答了“一个陈述”的问题。 @EJP 抱歉,您的回答让我更加困惑。我认为当我禁用自动提交并在执行我的语句后手动提交时,根本不会有竞争条件,因为它是一个事务(原子)。那我为什么需要SELECT ... FOR UPDATE @EJP 谢谢。一个问题:原子不是意味着事务不能被另一个事务中断-> 竞争条件是不可能的吗?那么,当我在单个事务中使用普通的SELECT 语句然后使用UPDATE 语句时,为什么它不起作用呢?

以上是关于Java MySQL 防止竞争条件的主要内容,如果未能解决你的问题,请参考以下文章

跨线程合并更改时如何防止竞争条件?

多个 NSManagedObjectContexts - 防止竞争条件和死锁

如何防止 Firestore 为预订按钮写入竞争条件

防止Rails中父关系中的竞争条件

如何使用 Qt Network 类防止关闭竞争条件

当用户连续执行多个graphql突变时防止竞争条件