【发布时间】: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。
标签: java mysql multithreading prepared-statement race-condition