Java.sql.Connection是否线程安全?

73
重新表述这个问题:我应该避免在不同的线程之间共享实现了 java.sql.Connection 接口的类的实例吗?

1
请参阅Java线程安全数据库连接 - Vadzim
5个回答

78
如果JDBC驱动符合规范,那么从技术上讲,该对象是线程安全的,但您应避免在线程之间共享连接,因为连接上的活动意味着一次只有一个线程能够执行任何操作。您应该使用连接池(如Apache Commons DBCP)来确保每个线程都有自己的连接。

6
例如,Postgres的实现没有同步访问autoCommit标志,因此不是线程安全的。 - Boris Pavlović
1
我脑海后面有个声音告诉我,JDBC规范要求所有java.sql对象都是线程安全的,但我找不到相关参考。 - skaffman
12
您可以参考http://java.sun.com/j2se/1.3/docs/guide/jdbc/spec/jdbc-spec.frame9.html中的内容,其中提到“我们要求所有java.sql对象上的所有操作都是多线程安全的,并且能够正确处理同时调用同一对象的多个线程。” - janko
1
@janko:就是他,谢谢,很高兴知道我没有疯掉。 - skaffman
25
在你引用的那篇Sun JDBC指南中,你应该引用最后加粗的那句话。我理解为他们承认多线程大部分是失败的,每个连接一个线程是当前的期望。"实际上,我们预计大多数JDBC对象只会以单线程方式访问。然而,一些多线程支持是必要的,我们在之前的草案中试图将某些类指定为MT安全和某些类指定为MT不安全,但似乎只增加了困惑而非启示。" - John M
1
JDBC规范[4.x](https://download.oracle.com/otndocs/jcp/jdbc-4_2-mrel2-spec/index.html)根本没有提到线程和线程安全。 - Roman-Stop RU aggression in UA

14

java.sql.Connection是一个接口。因此,它完全取决于驱动程序的实现,但通常情况下,您应该避免在不同线程之间共享相同的连接,并使用连接池。此外,建议将池中的连接数设置得比工作线程数更高。


9
接口是一份合约,而合约可以规定所有实现必须是线程安全的。只是对于java.sql.Connection而言,这并非如此。 - Wim Coenen
3
是的,接口是一个契约,您可以在描述接口的文档中添加一些附加要求,但正如您所说java.sql.Connection文档并没有定义线程安全要求,即使它定义了,线程安全仍然不是一种可以严格描述和强制执行的东西。实现可能仍然违反合同(有时是错误,有时是设计上的问题,例如IdentityHashMap)。 - Andrey Adamovich
@AndreyAdamovich建议将连接池中的连接数设置得比工作线程数更高。为什么?我的意思是,如果我在连接池中有很多连接,那么最终会遇到抖动问题。 - Amandeep Jiddewar
1
@AndreyAdamovich:线程安全绝对是可以设计和强制执行的。Java 不是特别擅长这两个方面,而且自从那条评论被写出来以来,我们已经看到了一些在执行分析方面更好的语言(比如 Golang)。 - Brian Bulkowski
1
@BrianBulkowski 我猜你不能真的使用Golang来编写J(ava)DBC驱动程序。 - Andrey Adamovich
@AndreyAdamovich 在软件中任何事情都是可能的,但似乎不明智 - 是的,我同意。 - Brian Bulkowski

4

Oracle JDBC和多线程文档:

因为所有的Oracle JDBC API方法都是同步的,如果两个线程同时尝试使用连接对象,则其中一个将被强制等待,直到另一个完成其使用。

因此,在Oracle的情况下可能是安全的,但并发访问会受到瓶颈的影响。


2
这在Oracle 8i中是正确的。在更新的版本中,我们有“控制串行访问连接(例如由连接缓存提供的)既是必要的又是鼓励的。然而,Oracle强烈反对在多个线程之间共享数据库连接。避免允许多个线程同时访问连接。如果多个线程必须共享连接,请使用纪律性的开始使用/结束使用协议。” - Roman-Stop RU aggression in UA

4
这是一个比较老的帖子,但对于那些寻找有关Microsoft SQL Server的答案的人来说,以下是答案:
SQLServerConnection不是线程安全的,但可以从单个连接创建多个语句在并发线程中同时处理。
SQLServerConnection实现了与SQL Server的JDBC连接。
从上述所有内容中,您可以共享语句而不是连接,并且如果您需要每个线程中的连接,可以使用线程池。
阅读更多 here

1
我们在WebSphere语句缓存的pooleddatasource上遇到了ArrayOutOfBoundsException问题,所以我们不得不禁用该缓存。
我们有一个阻塞自身的处理方式。
所有这些都是由于当前对连接的访问造成的,因此实践得出的结论是,你不能这样做。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接