1. 概述
多线程场景中,如果多个线程共享非线程安全的对象,线程的行为可能会乱套。
我们当中的大部分人都遭遇过线程安全问题。因此,我们不得不思考这个问题:“一个类是否线程安全?”
Java 应用程序是通过 JDBC 访问关系数据库的,其中难免涉及并发和多线程。在本文中,我们将讨论 java.sql.Connection是否是线程安全的。
2. java.sql.Connection接口
当我们通过 JDBC 访问数据库时,我们都会直接或间接使用java.sql.Connection对象。我们依靠这些连接对象来执行数据库操作。因此,java.sql.Connection是 JDBC 中非常重要的类型。
一个很常见的场景是,多个线程需要访问数据库。于是,我们经常听到这样的问题:“ java.sql.Connection线程安全吗?”
下面将仔细研究这个问题。此外,我们将讨论在多个线程之间使用java.sql.Connection对象的正确方法,以便多个线程可以同时访问数据库。
3.线程安全和java.sql.Connection
我们先简单聊聊线程安全。线程安全是一种编程方法。也就是说,它是一个与具体实现相关的概念。因此,我们可以使用不同的技术来实现线程安全,例如,无状态、不可变对象等等。
现在,让我们看看 java.sql.Connection。首先,它是一个接口——它不包含任何实现。因此,如果我们笼统地问:“ java.sql.Connection 线程安全吗?” 并没有多大意义。我们必须检查实现这个接口的类来决定一个实现是否是线程安全的。
那有哪些类实现了这个接口?它们是线程安全的吗?
我们通常不会在应用程序代码中实现java.sql.Connection接口。JDBC 驱动程序已经实现了这个接口,以便我们可以连接到特定数据库,如 SQL Server 或 Oracle。
因此,Connection实现的线程安全性完全取决于 JDBC 驱动程序。
接下来,我们将以两个数据库 JDBC 驱动程序作为例子。
4. java.sql.Connection实现示例
Microsoft SQL Server 和 Oracle Database 是两种广泛使用的关系数据库。
下面介绍这两个数据库的 JDBC 驱动程序,并讨论它们对 java.sql.Connection接口的实现是否是线程安全的。
4.1. 微软SQL服务器
根据其官方文档,Microsoft SQL Server 驱动程序类SQLServerConnection实现了java.sql.Connection接口,但它不是线程安全的:
SQLServerConnection不是线程安全的,但是从单个Connection创建的多个Statement可以在并发线程中被共享。
所以,我们不能在线程之间共享SQLServerConnection对象,但我们可以共享从同一个SQLServerConnection对象创建的Statement。
接下来,我们再来看看另一个知名的数据库产品——Oracle 数据库。
4.2. 甲骨文数据库
官方的 Oracle JDBC 驱动程序以线程安全的方式实现了java.sql.Connection接口。
Oracle在其官方文档中声明了其Connection实现的线程安全性:
Oracle JDBC 驱动程序为使用 Java 多线程的应用程序提供全面支持并对其进行了高度优化……
但是,Oracle 强烈反对在多个线程之间共享数据库连接。避免多个线程同时访问一个连接……
因此,Oracle 的连接实现是线程安全的。但是,官方 “非常不鼓励”在多个线程之间共享连接对象。
因此,从 SQL Server 和 Oracle 的例子中,我们知道不能假设java.sql.Connection的实现是线程安全的。那如果我们想要多个线程同时访问一个数据库,正确的做法是什么?
5. 使用连接池
当我们从应用程序访问数据库时,我们首先需要建立与数据库的连接。这是很昂贵的操作。为了提高性能,通常我们会使用连接池。
下面我们快速了解连接池在多线程场景中是如何工作的。
一个连接池包含多个连接对象。我们可以配置池的大小。
当多个线程需要并发访问一个数据库时,它们会从连接池中请求连接对象。
如果池中仍有空闲连接,线程将获取连接对象并开始其数据库操作。线程完成工作后,会将连接返回到池中。
如果池中没有空闲连接,线程将等待另一个线程将连接对象返回到池中。
因此,连接池允许多个线程使用不同的连接对象并发访问数据库,而不是共享同一个。这样我们就不用关心Connection接口的实现是否是线程安全的了。
六,结论
在本文中,我们讨论了常见问题:java.sql.Connection线程安全吗?
由于java.sql.Connection是一个接口,因此很难预测这些实现是否是线程安全的。
此外,如果需要多个线程同时访问数据库,连接池是处理连接的正确方法。