C3P0是线程安全的吗?

时间:2011-02-16 14:17:56

标签: java mysql jdbc thread-safety c3p0

当我尝试在MySQL数据库上使用C3P0执行一些简单的读取(SELECT)操作时,会发生中断异常(java.lang.InterruptedException)。当我将并行线程数增加到100以上(我尝试过5,10,20,60和100)时会发生异常。我执行的语句很简单:

SELECT `Model.id` FROM `Model` LIMIT 100;

我的连接是从使用以下属性配置的ComboPooledDataSource汇集的(另请参阅C3P0 manual):

c3p0.jdbcUrl=jdbc:mysql... 
c3p0.debugUnreturnedConnectionStackTraces=true
c3p0.maxIdleTime=5
c3p0.maxPoolSize=1000
c3p0.minPoolSize=5
c3p0.initialPoolSize=5
c3p0.acquireIncrement=3
c3p0.acquireRetryAttempts=50
c3p0.numHelperThreads=20
c3p0.checkoutTimeout=0
c3p0.testConnectionOnCheckin=true
c3p0.testConnectionOnCheckout=true
user=***
password=***

我运行测试的计算机上的MySQL服务器配置为接受1024个连接,并且我运行的单元测试成功执行(数据按预期从数据库中检索)。但是,在C3P0日志文件中,我发现以下警告:

15:36:11,449  WARN BasicResourcePool:1876 - com.mchange.v2.resourcepool.BasicResourcePool@9ba6076 -- Thread unexpectedly interrupted while performing an acquisition attempt.
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask.run(BasicResourcePool.java:1805)
    at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:547)

我想知道这个警告的原因,其次是它对软件稳健性和稳定性的可能影响。请注意,在使用之后,我会关闭结果集,SQL语句和连接。最后,一旦测试结束,我通过调用方法ComboPooledDataSource#close()来关闭池。更奇怪(似乎是揭示同步问题),如果我使用以下内容给游泳池足够的时间...

Thread.sleep(10000); // wait for some time
datasource.close();

日志中不会出现任何警告!你觉得这会给C3P0带来一个线程安全问题,还是我做错了什么?

更新1:

让我提一下,除了已经提到的内容之外,删除Thread.sleep(10000)会导致在MySQL日志文件中记录以下信息:

110221 14:57:13 [Warning] Aborted connection 9762 to db: 'myDatabase' user: 'root'
host: 'localhost' (Got an error reading communication packets)

可能会有更多的光......

更新2:

这是我的MySQL服务器配置。服务器允许的最大连接数设置为1024(如上所述),这足以满足我的目的。

[mysqld]
max_allowed_packet  = 64M
thread_concurrency      = 8
thread_cache_size       = 8
thread_stack        = 192K
query_cache_size = 0
query_cache_type = 0
max_connections = 1024
back_log = 50
innodb_thread_concurrency = 6
innodb_lock_wait_timeout = 120
log_warnings

为了模糊任何疑问,我通过以下方式验证了最大连接数:

show global variables where Variable_name='max_connections';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 1024  | 
+-----------------+-------+
1 row in set (0.00 sec)

3 个答案:

答案 0 :(得分:6)

该警告来自2007年左右here。 这似乎是试图获取连接的线程。

也许是因为池设置为获取比mysql服务器配置处理的连接更多的连接。这似乎是有道理的,因为默认max_connection是100(或151取决于你的mysql版本)

因此尝试获取连接的线程进入尝试获取的sleep()/ retry循环 连接 - 但是当它在循环内部时关闭整个池 - 该线程被中断,因此当您关闭池时可以回收所有资源。

到目前为止,似乎没有任何损害,当你完成它时,你的代码可能会返回到池中的连接,让它们闲置供其他人使用,并且你的所有查询都会通过。

答案 1 :(得分:1)

也许,InterruptedException是正常的,因为一些c3p0线程正在等待连接,当你调用close()这些线程被中断时。虽然,根据您的设置(100个客户端,1000个服务器连接),等待资源的这种必要性并不那么明显 如果你真的感兴趣,最可靠的解决方案是寻找c3p0日志,也许,增加一些日志并重新编译......

答案 2 :(得分:1)

我刚遇到这个问题。这是我对DataSource的设置:

 [java:comp/env/jdbc/pooledDS] = [com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 2siwtu8o4m410i1l4tkxb|187c55c, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> null, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 2siwtu8o4m410i1l4tkxb|187c55c, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> null, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]]

并修复:

[java:comp/env/jdbc/pooledDS] = [com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 2siwtu8o4m5kux117kgtx|13e754f, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> oracle.jdbc.driver.OracleDriver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 2siwtu8o4m5kux117kgtx|13e754f, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:oracle:thin:@localhost:1521:oracle, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]]

所以并非一切都设置正确。更具体地说,当我调用setDriverClasssetJdbcUrl来纠正空值时,我消除了InterruptedException