当前位置:编程学习 > JAVA >>

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

比较苦恼,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秒,对数据采样结果的影响微乎其微。
其他项目要看具体情况了。


虽有拙漏之处,还望能有点用!
补充:Java ,  Java相关
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,