日期:2014-05-16 浏览次数:20548 次
数据库连接池是java软件常用的组件,但对数据库连接池不正确的使用会造成软件的死锁。
死锁的产生原理是:存在线程同时占有两个以上的连接对象的情况。
比如某连接池中有2个连接对象。
有两个线程分别占用了一个,又分别来申请另一个。这时由于连接池空,而两个线程本身都卡在连接池上,不可能释放原来占有的连接对象,这时产生死锁。
连接池死锁,会导致软件和数据库相关的部份无响应。比如登陆功能,会用数据库做用户校验,发生连接池死锁后,登陆功能会无法使用(点登陆按钮后,长时间无相应)。
连接池死锁的问题可以参看以下文章:
http://publib.boulder.ibm.com/infocenter/iseries/v5r3/index.jsp?topic=%2Frzatz%2F51%2Fprogram%2Fdatapool.htm
解决方法:一个线程中同时只允许使用一个连接对象。
一段数据库逻辑通常只需要一个连接对象,因此在一个方法中实现这点是比较容易的。但是当发生如下情形时则比较麻烦:
有两个方法A和B,A和B都要使用数据库连接,而A在事务中调用了B。
为了解决这个问题有如下两种办法:
一是,将连接对象放在线程的ThreadLocal中。这样A和B即可共享连接对象了。但这种方法需要考虑嵌套事务的问题。数据库是不允许嵌套事务的。因此,要么避免嵌套事务,要么自动合并事务。
注意:采用传参的形式也可以做到这点,但只能就问题解决问题,不是框架层面的解决方案,也要处理嵌套事务的问题。
二是,直接人为在代码上保证一个线程不同时占有两个连接对象。幸好这样的情况从经验上看并不多,只要找到这样的方法,想办法拆分一下逻辑,将调用的部份从事务中拿出来即可。
???? 还有一种古怪的办法,就是新起一个线程来调用B。如果A对B的返回值有依赖关系,这种办法是行不通的,不推荐使用。
??? 有人认为只要给连接池设置一个超时就行了。可是,这样做并不恰当。因为,连接是应该可以共享的,如果暂时,连接池内没有连接可用,应该让申请者等待可用的连接,而不是抛出超时异常,这样会导致申请者整个过程的失败。
?
?
2012-02-02
?
新发现了一种由连接池引起的死锁情况,并不是由一个线程占用两个以上的连接引起的,是因为线程取得连接后被锁卡住不能归还引起的方式如下:
?
连接池中有一个连接对象。
?
A线程:
取得连接对象
开始事务
synchronized(cache){
??? 数据库代码
}
提交
关连接
?
B线程:
synchronized(cache){
?? 取得连接,操作数据库。
}
?
A线程执行卡在同步块外,此时连接池为空。
B线程进入同步块,在取得连接时卡在连接池上。
?
?
?
?