• 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之间。当然这里的这个值是根据它设定的生存时间来决定的。

  • 相关阅读:
    字节流与字符流,字节流和字符流的使用哪个多?
    java 读写操作大文件 BufferedReader和RandomAccessFile
    hibernate官网文档
    阿里云centos怎么用xshell5登陆
    STM8L使用外部8M HSE
    vscode函数注释添加【转载】
    某个通信的异常判断存在于两个任务中时计算通信超时的一种思路
    STM32F0芯片读保护
    FrameworkCubeMX.gpdsc missing的问题
    git忽略已经提交的文件【转载】
  • 原文地址:https://www.cnblogs.com/caoxb/p/10032141.html
Copyright © 2020-2023  润新知