zoukankan      html  css  js  c++  java
  • 简单的数据库连接池实现

    开发web,离不开对数据库的操作。每次JDBC操作,都会用到Connection。如果没一次数据库操作都新建一个Connection,会造成较大的系统开销。数据库连接池就是用来解决这个问题。

    数据库连接池的基本思路就是,我先初始化一些Connection,当需要连接时,从连接池里取,如果没有空闲连接就新建一个连接,如果达到连接池设置的上限,则等待其他连接的释放。

    像C3P0之类的连接池,都支持配置,如上线多少个,如果新建一次性增加多少个,初始化时多少个。每隔多久回收一次等等策略。

    下面来所说我的实现:

    连接池实现类

    /*
     * To change this template, choose Tools | Templates
     * and open the template in the editor.
     */
    package com.yz.db.pool;
     
    import com.yz.db.datasource.DataSourceBean;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import org.apache.log4j.Logger;
     
    /**
     *
     * @author yingzi.zhu
     */
    public class DBConnectionPool {
     
        private static final Logger logger = Logger.getLogger(DBConnectionPool.class);
        //连接池在无空闲连接可用时一次性创建的新数据库连接数
        private int acquireIncrement = 3;
        //连接池初始化时创建的连接数
        private int initialPoolSize = 3;
        //连接池中拥有的最大连接数,如果获得新连接时会使连接总数超过这个值则不会再获取新连接,而是等待其他连接释放,所以这个值有可能会设计地很大
        private int maxPoolSize = 15;
        //连接的最大空闲时间,如果超过这个时间,某个数据库连接还没有被使用,则会断开掉这个连接如果为0,则永远不会断开连接
        private int maxIdleTime = 0;
        //连接池保持的最小连接数,后面的maxIdleTimeExcessConnections跟这个配合使用来减轻连接池的负载
        private int minPoolSize = 0;
        private int currentSize = 0;
        private DataSourceBean dataSource;
        private ArrayList<Connection> freeConnectionList = new ArrayList<Connection>();
        private ArrayList<Connection> usedConnectionList = new ArrayList<Connection>();
     
        public DBConnectionPool() {
        }
     
        public DBConnectionPool(DataSourceBean dataSource, int acquireIncrement, int initialPoolSize, int maxPoolSize, int maxIdleTime, int minPoolSize) {
            this.dataSource = dataSource;
            this.acquireIncrement = acquireIncrement;
            this.initialPoolSize = initialPoolSize;
            this.maxPoolSize = maxPoolSize;
            this.maxIdleTime = maxIdleTime;
            this.minPoolSize = minPoolSize;
     
            createConnection(initialPoolSize);
        }
     
        /**
         * 得到Connection
         *
         * @return
         */
        public synchronized Connection getConnection() {
            Connection conn = null;
            while (currentSize >= maxPoolSize && freeConnectionList.isEmpty()) {
                try {
                    logger.info("达到连接上线,等待其他程序释放连接");
                    wait();
                } catch (InterruptedException ex) {
                    logger.error("线程异常" + ex.getMessage());
                }
            }
     
            if (freeConnectionList.size() > 0) {
                conn = freeConnectionList.remove(0);
                usedConnectionList.add(conn);
                System.out.println(currentSize + " " + usedConnectionList.size() + " " + freeConnectionList.size());
                return conn;
            }
     
            if (currentSize < maxPoolSize) {
                createConnection(acquireIncrement);
                if (freeConnectionList.isEmpty()) {
                    return getConnection();
                }
                conn = freeConnectionList.remove(0);
     
                usedConnectionList.add(conn);
                System.out.println(currentSize + " " + usedConnectionList.size() + " " + freeConnectionList.size());
                return conn;
            }
     
            return conn;
        }
     
        public int getPooSize() {
            return currentSize;
        }
     
        public int getFreeConnSize() {
            return freeConnectionList.size();
        }
     
        public int getUsedConnSize() {
            return usedConnectionList.size();
        }
     
        /**
         * 从DriverManager中新建一个链接
         *
         * @return
         */
        private Connection newConnection() {
            Connection conn = null;
            try {
                Class.forName(dataSource.getDriver());
                conn = DriverManager.getConnection(dataSource.getUrl(), dataSource.getUser(), dataSource.getPassword());
                return conn;
            } catch (ClassNotFoundException ex) {
                logger.error("注册驱动类异常" + ex.getMessage());
            } catch (SQLException ex) {
                logger.error("获得连接异常" + ex.getMessage());
            }
            return conn;
        }
     
        /**
         * 释放连接
         *
         * @param conn
         */
        public synchronized void freeConnection(Connection conn) {
            usedConnectionList.remove(conn);
            freeConnectionList.add(conn);
            notifyAll();
        }
     
        /**
         * 验证连接是否可用,不可用则删除
         *
         * @param conn
         */
        public synchronized void isValid(Connection conn) {
            try {
                if (!conn.isValid(3000)) {
                    if (freeConnectionList.contains(conn)) {
                        freeConnectionList.remove(conn);
                    }
                    if (usedConnectionList.contains(conn)) {
                        usedConnectionList.remove(conn);
                    }
     
                    conn.close();
                    currentSize--;
                }
            } catch (SQLException ex) {
                logger.error("数据库异常" + ex.getMessage());
            }
        }
     
        /**
         * 初始化最小连接
         */
        private synchronized void createConnection(int count) {
            for (int i = 0; i < count; i++) {
                if (currentSize < maxPoolSize) {
                    freeConnectionList.add(newConnection());
                    currentSize++;
                }
            }
        }
     
        
        public void releaseAllConnection() {
            try {
                for (int i = 0; i < freeConnectionList.size(); i++) {
                    freeConnectionList.get(i).close();
                }
                if (usedConnectionList.size() > 0) {
                    try {
                        Thread.currentThread().sleep(100);
                    } catch (InterruptedException ex) {
                        logger.error(ex.getMessage());
                    }
                    releaseAllConnection();
                }
            } catch (SQLException ex) {
                logger.error("数据库关闭连接异常" + ex.getMessage());
            }
        }
    }

    这个是连接池管理的核心类。里面的操作也很简单,声明一些属性用来保存配置项,声明两个List用来存放正在使用的连接和空闲连接。根据配置来判断,怎么去获取一个连接,怎么释放一个连接,什么时候新建一个连接等等。最主要的是考虑线程安全问题。如果没有,且已经达到上限,则需要等待其他线程的释放。

    连接池管理类

    /**
     *
     * @author yingzi.zhu
     */
    public class DBConnectionManager {
     
        private static DBConnectionManager instance = null;
        private ConcurrentHashMap<String, DBConnectionPool> pools = new ConcurrentHashMap<String, DBConnectionPool>();
     
        private DBConnectionManager() {
        }
     
        public static DBConnectionManager getInstance() {
            if (instance == null) {
                synchronized (DBConnectionManager.class) {
                    if (instance == null) {
                        instance = new DBConnectionManager();
                    }
                }
            }
            return instance;
        }
     
        /**
         * 
         * @param dsConfig 
         */
        public void createPool(DsConfig dsConfig){
            DataSourceBean bean = new DataSourceBean();
            bean.setDriver(dsConfig.getDriver());
            bean.setUrl(dsConfig.getUrl());
            bean.setPassword(dsConfig.getPassword());
            bean.setUser(dsConfig.getUser());
            
            DBConnectionPool pool = new DBConnectionPool(bean, dsConfig.getAcquireIncrement(), dsConfig.getInitialPoolSize(), dsConfig.getMaxPoolSize(), dsConfig.getMaxIdleTime(), dsConfig.getMinPoolSize());
            pools.put(dsConfig.getConnName(), pool);
        }
        
        public Connection getConnection(String connName){
            DBConnectionPool pool = pools.get(connName);
            if(pool == null){
                throw new RuntimeException("没有该连接池!");
            }
            return pool.getConnection();
        }
        
        public void freeConnection(Connection conn, String connName){
            DBConnectionPool pool = pools.get(connName);
            if(pool == null){
                throw new RuntimeException("没有该连接池!");
            }
            pool.freeConnection(conn);
        }
        
        public int getPoolSize(String connName){
            DBConnectionPool pool = pools.get(connName);
            if(pool == null){
                throw new RuntimeException("没有该连接池!");
            }
            return pool.getPooSize();
        }
        
        public int getFreeConnSize(String connName){
            DBConnectionPool pool = pools.get(connName);
            if(pool == null){
                throw new RuntimeException("没有该连接池!");
            }
            return pool.getFreeConnSize();
        }
        
        public int getUsedConnSize(String connName){
            DBConnectionPool pool = pools.get(connName);
            if(pool == null){
                throw new RuntimeException("没有该连接池!");
            }
            return pool.getUsedConnSize();
        }    
        
    }
     
    该类实现了对线程池的管理,一般一个数据源简历一个pool,然后manager类管理多个pool
     
    下面是一个简单的测试:
     
    public static void main(String[] args) throws SQLException {
        DsConfig config = new DsConfig();
        config.setConnName("conn1");
        config.setAcquireIncrement(3);
        config.setDriver("com.mysql.jdbc.Driver");
        config.setInitialPoolSize(3);
        config.setMaxIdleTime(0);
        config.setMaxPoolSize(15);
        config.setMinPoolSize(3);
        config.setUrl("jdbc:mysql://localhost:3306/world");
        config.setUser("root");
        config.setPassword("123456");
     
        final DBConnectionManager mg = DBConnectionManager.getInstance();
        mg.createPool(config);
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Connection connection = mg.getConnection("conn1");
                        Statement createStatement = connection.createStatement();
                        ResultSet executeQuery = createStatement.executeQuery("select * from city");
                        Thread.sleep(100);
                        mg.freeConnection(connection, "conn1");
                    } catch (SQLException ex) {
                        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
                    } 
                    catch (InterruptedException ex){
     
                    }
                }
            }).start();
        }
    }

    分别获取100个链接,查看日志输出,全部正常。但是该线程池管理类比较简单,如果达到上限,则不再新增。使连接一直保持在最大限额。也不释放。其实也可以加一个定时程序,每隔多久释放一些符合可以释放条件的连接。

  • 相关阅读:
    [编程题] 微信红包
    MYSQL实现主从复制
    有关windows系统的EXE和DLL文件说法错误
    Http错误代码
    一步步优化JVM四:决定Java堆的大小以及内存占用
    一步步优化JVM三:GC优化基础
    一步步优化JVM二:JVM部署模型和JVM Runtime
    一步步优化JVM一:概述、方法及需求
    排查Java线上服务故障的方法和实例分析
    【转】Zookeeper-Watcher机制与异步调用原理
  • 原文地址:https://www.cnblogs.com/atio/p/3469222.html
Copyright © 2011-2022 走看看