多线程环境中的 JDBC 事务

Posted

技术标签:

【中文标题】多线程环境中的 JDBC 事务【英文标题】:JDBC transactions in multi-threaded environment 【发布时间】:2018-02-23 08:51:47 【问题描述】:

开发一个Java应用程序在多个线程之间共享一个Connection,就会出现并发问题。

如果线程 A 更新表 T 中的记录 1,同时线程 B 对表 T 中的记录 1 发出 SELECT,我如何确保线程 B 读取线程 A 的更新值?

java.sql.Connection 提供了带有 begin()、commit() 和 rollback() 的事务,但是这个过程是否也包括数据正确性?

我想我错过了什么。

【问题讨论】:

使用知名的连接池 不要分享Connection。它不是用来分享的。 【参考方案1】:

两点:

    您不应该在线程之间共享jdbc.Connection,至少对于任何“认真生产”的代码,请参阅here。出于演示目的,我认为共享连接是可以的; 如果一个线程在相关 DB 事务提交后从 DB 中读取数据,它将看到另一个线程写入的数据。

关于你的第二个问题

线程 B 会超时,直到第一个事务有 commit() 或 rollback()

-- B 将阻塞直到A tx 完成(通过提交或回滚)如果:

    B 尝试更新/删除由A 更新的同一表行,并且... A 使用 SELECT ... FOR UPDATE 在 DB 级锁定下更新该行。

您可以使用两个控制台(例如,使用 PostgreSQL psql)获得此行为,每个控制台代表一个线程:

A 控制台输入以下:

BEGIN;
SELECT some_col FROM some_tbl WHERE some_col = some_val FOR UPDATE;

现在在B 控制台输入:

BEGIN;
UPDATE some_tbl SET some_col = new_val WHERE some_col = some_val;

您应该看到UPDATE 阻塞,直到在A 您执行COMMITROLLBACK

上面的解释使用了单独的 DB 连接,就像 Java JDBC 连接池一样。我认为,当您在 Java 线程之间共享单个连接时,如果连接被其他线程使用,与 DB 的任何交互都会阻塞。

【讨论】:

它实际上是一个小的演示应用程序。我缺少的是:如果我在线程 A 中开始()一个事务,并且在我开始()来自线程 B 的另一个事务之后,线程 B 是否会超时,直到第一个事务具有 commit() 或 rollback() ?还是线程 B 会抛出 SQLException? 3. Java 中的事务是用一个线程使用连接的想法来实现的。当事务打开时,底层实现调用在连接上开始。成功返回时调用commit,如果有运行时异常调用rollback。 @BorisPavlović 理解这个概念。现在又出现了一个问题。从线程 A(使用连接 A)开始事务(调用 begin())是否会锁定相关记录,直到提交或回滚更改?或者线程 B(带有连接 B)是否仍然能够在不等待线程 A 提交/回滚的情况下选择这些记录? 我会说后者,但是,当有一个简单的解决方案来避免风险时,它是无关紧要的。 @BorisPavlović 可能我把事情复杂化了。这个简单的解决方案是什么?每个线程一个连接?【参考方案2】:

Jdbc 是一种被广泛采用的标准,但遵守程度参差不齐,因此对什么是安全的进行笼统的陈述可能并不好。

我不希望有任何东西可以防止由多个线程进行的语句执行、提交和回滚被交错。最好的情况是,一次只有一个线程可以使用连接,其他线程阻塞,使多线程无用。

如果您不想为每个线程提供连接,您可以让线程将工作项提交到一个队列,该队列由处理所有 jdbc 工作的单个工作线程使用。但是引入连接池对现有代码的影响可能较小。

一般来说,如果您有并发更新和读取,那么它们会按照它们发生的顺序发生。锁定和隔离级别为并发事务提供一致性保证,但如果尚未开始其事务,则这些不适用。您可以在每一行上使用状态标志、版本号或时间戳来指示更新发生的时间。

如果您有很多更新,最好将它们收集到一个平面文件中并执行批量复制。它可以比使用 jdbc 快得多。然后在 jdbc 中执行选择更新。

【讨论】:

从技术上讲,JDBC 要求连接是线程安全的,但这并不意味着这些线程是相互隔离的。这仍然意味着所有这些线程应该在同一个事务中合作,并协调它们的动作,这样它们就不会做干扰的工作(例如,一个线程提交事务,而另一个线程仍在工作,等等)。这使得从多个线程访问单个连接充其量不是很有用,最坏的情况是成为竞争条件出没的错误的避风港。

以上是关于多线程环境中的 JDBC 事务的主要内容,如果未能解决你的问题,请参考以下文章

Spring在多线程环境下如何确保事务一致性

Spring在多线程环境下如何确保事务一致性

JFinal中的事务开启多线程后是不是有效

JDBC&&c3p0事务批处理多线程 于一体的经典秘方QueryRunner (common-dbutils)

java事务处理

多线程环境下重用ResultSet成员变量