C3P0 bug 解决办法: 关闭的连接 still in use

toTheDeath 2012-11-22 09:16:35
比较苦恼,c3p0跑着跑着就出现这个问题,我这里的出现周期基本上在7-8天一次。我用的是多线程任务访问队列,然后任务对象中调用数据库操作的方式,即队列中不断有新的任务需要处理,长则几秒,短则毫秒级。当第二次出现此问题的时候,我再也忍不住了,改了一下程序,近期观察了效果不错,应该可以说是彻底解决了。以下仅供参考:
数据库连接池管理对象:
import java.sql.Connection;
import java.sql.SQLException;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class DBConnectionManager{

public static boolean inReseting = false;//重置时,短时保护
public static boolean inProtecting = false;//重置后,长时保护
private static ComboPooledDataSource cpds = null;
private static Connection connection = null;

public static void init(){
// 建立数据库连接池
// 初始化连接池
//url , username,psw设置
//连接池各初始化参数设置
//略去n行代码
}

public static Connection getConnection(){
try {
if (cpds == null){
init();
}
if (connection==null) {
connection = cpds.getConnection();
}
} catch (SQLException ex) {
ex.printStackTrace();
}
return connection;
}

/**
* 重置连接池
*/
public synchronized static void reset() throws Exception{
try{
if(cpds==null)return;
inReseting = true;
inProtecting = true;
//SmsBase.SendSms("150xxxxxxxxx","server c3p0 try to reset");//短信通知自己bug出现了,需要重置连接池
System.out.println("c3p0 stopped,waiting for 10 seconds to soft restart");
Thread.sleep(10000);
cpds.softResetAllUsers();//如果此软重置仍然不生效,可以再试试换成softResetDefaultUser()或者直接硬重置hardReset(),即销毁池再新建
System.out.println("c3p0 soft restarted..");
inReseting = false;
//SmsBase.SendSms("150xxxxxxxxx","server c3p0 reseted");//短信通知自己bug出现了,重置连接池完毕
Thread.sleep(120000);//设置120秒保护,以免其他线程短时间内出同样错误后,不断重置连接池
}catch(Exception e){
//软重置出错时,考虑硬重置
//不过由于软重置前连接池inReseting属性已经设置为保护了,阻止访问程序拿到更多的连接
//所以一般软重置不会出错,以下代码只是以防万一
System.out.println("c3p0 stopped,waiting for 10 seconds to hard restart");
Thread.sleep(10000);
cpds.hardReset();
System.out.println("c3p0 restarted..");
inReseting = false;
System.out.println("server c3p0 closed,hard restarted");
//SmsBase.SendSms("15021852100","server c3p0 hard restarted");//短信通知自己bug出现了,硬重置完毕
Thread.sleep(120000);//设置120秒保护,以免其他线程短时间内出同样错误后,不断重置连接池
}finally{
//不管如何,完成后将短长保护都取消,以便其它线程继续使用连接池,如果还报出错误,则进入下一次重置动作
inProtecting = false;
inReseting = false;
}
}


public static ComboPooledDataSource getCpds() {
return cpds;
}

public static void main(String[] args) {
getConnection();
}
}

这里是队列弹出元素并丢给多线程池去处理,此处列出代码仅仅是出于方便理解后续的代码
public class LocalDataCacheRunnable implements Runnable {
public void run() {
try {
ThreadPoolExecutor pool = ThreadPools.getThreadPool();
while(Console.running){
if(!LocalDataCache.isEmpty()){
pool.execute(new FrameTask());
}
Thread.sleep(0,3000);
}
pool = null;
} catch (Exception e) {
e.printStackTrace();
}
}
}

以下是FrameTask类中具体执行数据库操作的代码:
public static void savePrtocolData(Frame input)throws Exception {
CallableStatement call = null;
try{
if(DBConnectionManager.inReseting==true)return;//如果数据库连接池正在重启,则放弃执行任务
Connection conn = DBConnectionManager.getConnection();
call = conn.prepareCall("{call SP_1(?,.....)}");
execEnergy(call);
}catch(Exception e){
e.printStackTrace();
if(e instanceof SQLRecoverableException){//不知道各位看官后台抛的是不是这个类型的异常,反正我这儿是。
if(DBConnectionManager.inProtecting==false){//这行是保护连接池不要被多线程并发时重置多次。
DBConnectionManager.reset();
}
}
}finally{
if(call!=null)
call.close();
}
}
这儿也只是很菜的一个办法,就是当bug出现时,先中断连接池的使用,给它一定时间去重置,重置完后重新解锁以供继续使用。因为我做的是数据样本采集类的软件,所以中断10秒,对数据采样结果的影响微乎其微。
其他项目要看具体情况了。


虽有拙漏之处,还望能有点用!
...全文
422 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

50,528

社区成员

发帖
与我相关
我的任务
社区描述
Java相关技术讨论
javaspring bootspring cloud 技术论坛(原bbs)
社区管理员
  • Java相关社区
  • 小虚竹
  • 谙忆
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧