JDBC连接池的简单实现

实现一个简单的数据库连接池

 

前言:

  最近在搞一个项目,就是要把SqlServer数据全部导入到Oracle中,也要让项目由原来的SqlServer支持Oracle,于是自已在网上找了很多工具,发现导的时候都有问题,而且数据量非常庞大。一开始是自已手动导,将SqlServer数据库导成支持Oracle的sql文件,然后再把这个sql文件导入到Oracle中,发现将一个10万条的sql文件导入到Oracle中都要半小时,简直崩溃了!  想想单个导sql文件的方式属于单线程模式,因为使用PLSQL工具导是只有一个连接,于是就想到了使用多线程的方式导,也就是采用多个连接,多个子任务去导。因此便使用到了资源池的这种模式。使用多线程、资源池方式之后,速度提升了上千倍。

 

实现思路(如下图所示): 

 

说明:

使用一个池也可实现资源池(即Map<Connetion, Params> pool 这种方式)但是这种逻辑复杂一点,即一个pool中要保证不连接数不能超过最大值又要判断哪些连接已经被占用。而我这里采用两个池来实现,一个是Used池,用来存放正在连接的资源;一个是Pool池,用来存放已经释放连接的资源;这样逻辑清晰简单。

 

实现步骤: 

下面就是数据库连接池的简单实现方式:

 

1.编写一个对象池的抽象类

package com.core.jdbc;


import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

public abstract class ObjectPool<T> {

    /**
     * 创建对象
     */
    protected abstract T create();

    /**
     * 验证对象有效性
     */
    public abstract boolean validate(T t, long createTime);

    /**
     * 使对象失效
     */
    public abstract void expire(T t);

    private ConcurrentHashMap<T, Long> used; // 正在使用的,保存新建的资源
    private ConcurrentHashMap<T, Long> pool; // 未被使用的,保存释放的、未失效的资源,池子里面保存有效可用的对象

    private static int MAX_CONNECT_SIZE = 100;         // 最大连接数(这里也是pool的最大size,虽然没有定义pool的最大size,但是从整个逻辑上讲,pool的size是小于这个值的)
    public static int MAX_KEEP_ALIVE_TIME = 3000;      // 最大生存时间,这个时间也决定了创建连接的频率

    /**
     * 获得资源
     */
    public synchronized T getResource() {
        T t = null;
        // 初始化
        if (null == pool) {
            pool = new ConcurrentHashMap<T, Long>();
        }
        if (null == used) {
            used = new ConcurrentHashMap<T, Long>();
        }
        
        // 超过最大连接数,等待(注意:这里可以加一个拒绝策略,即超时拒绝连接还是继续等待)
        while (used.size() >= MAX_CONNECT_SIZE) {
            try {
                this.wait(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } 
        
        // 默认先从池子里面去取
        if (pool.size() > 0) {
            for (Entry<T, Long> entry : pool.entrySet()) {
                t = entry.getKey();
                if (null!=t) {
                    break;
                }
            }
            pool.remove(t);
        } 
        // 在池子中未取到,创建一个新的
        if (null == t) {
            t = create();
        }
        used.put(t, System.currentTimeMillis());
        this.notifyAll();
        return t;
    }

    /**
     * 释放某个资源
     */
    public synchronized void release(T t) {
        if (null==t) {
            return;
        }
        
        while (used.size()==0) {
            try {
                this.wait(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 判断是否过有效期
        if (validate(t, used.get(t))) {
            // 放入池中
            pool.put(t, System.currentTimeMillis());
            used.remove(t);
        } else {
            expire(t);
            used.remove(t);
        }
        
        this.notifyAll();
    }

}

 

2.编写数据库连接池的具体实现类

public class ConnectionPool extends ObjectPool<Connection> {
    private static int count = 0;

    public ConnectionPool() {
        try {
            Class.forName("oracle.jdbc.driver.OracleDriver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected Connection create() {
        Connection conn = null;
        try {
            conn = DriverManager.getConnection("jdbc:oracle:thin:@//127.0.0.1:1521/SZNY", "caoxiaobo", "123456");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        count ++;
        logger.debug("建立连接次数" +count);
        return conn;
    }

    @Override
    public boolean validate(Connection o, long createTime) {
        if (System.currentTimeMillis() - createTime > MAX_KEEP_ALIVE_TIME)
            return false;
        return true;
    }

    @Override
    public void expire(Connection o) {
        try {
            o.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

 

 

3.编写JDBC连接池单例类来确保只有一个池(确保ConnectionPool 对象唯一,即程序中所有的连接都从一个pool中去取)

public class JdbcConnection {

    private JdbcConnection () {
        
    }
    
    private static class Singleton {
        private static ConnectionPool pool = new ConnectionPool();
    }
    
    public static Connection getConnection() {
        return Singleton.pool.getResource();
    }

    public static void release(Connection conn) {
        Singleton.pool.release(conn);
    }

}

 

 

4.并发测试类:

public class PoolTest {
    
    public static void main(String[] args) {

        Runnable run = new Runnable() {
            
            @Override
            public void run() {
                Connection conn = JdbcConnection.getConnection();
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                JdbcConnection.release(conn);
            }
        };
     // 创建2000个线程,模拟并发
for (int i=0; i<2000; i++) { Thread thread = new Thread(run); thread.start(); } } }

 

测试结果:

假设并发请求有2000个(假设数据库最大连接数为150,这里设置要比它正常值小一点,即100),如果不使用资源池,那么就需要不断的创建、销毁2000次连接,对于服务器的性能来说影响还是比较大的。通过这个示例,我们可以看到这个结果(创建、销毁)远远小于2000次,大概测试了一下平均100-120之间。当然这里的这个值是根据它设定的生存时间来决定的。

 

posted @ 2018-11-28 14:55  cao_xiaobo  阅读(2531)  评论(0编辑  收藏  举报