数据库连接池怎么实现_java数据库连接池原理

数据库连接池怎么实现_java数据库连接池原理数据库连接池1.数据库连接池是干什么的假如我们有个应用程序需要每隔10秒查询一次数据库,我们可以用以下方式方法1:每次查询的时候都新建一个数据库连接,查询结束关闭数据库连接。由于数据库连接的建立是一个非常耗费资源的过程,所以这种每次都新建连接的方式非常浪费资源,不可取。方法2:在最开始的新建一个数据库连接,后续过程中一直使用这个数据库连接进行查询,直到最后关

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

数据库连接池

1. 数据库连接池是干什么的

假如我们有个应用程序需要每隔10秒查询一次数据库,我们可以用以下方式

方法1:每次查询的时候都新建一个数据库连接,查询结束关闭数据库连接。

由于数据库连接的建立是一个非常耗费资源的过程,所以这种每次都新建连接的方式非常浪费资源,不可取。

方法2:在最开始的新建一个数据库连接,后续过程中一直使用这个数据库连接进行查询,直到最后关闭

这种方式虽然减少了新建数据库的资源消耗。但是对于一个数据库连接,每10秒才使用一次,也是非常大的浪费。而且假如有一千个应用程序在同时运行,难道就要同时打开一千个数据库连接?

方法3:我们在开始的时候根据需要同时打开多个数据库连接放到一个容器中,每次应用程序进行查询的时候从容器中取出一个数据库连接进行查询,查询完毕放回容器。

方法3即没有重复新建数据库连接,也保证了每个数据库连接的使用率,其中所说的容器就是数据库连接池。

2. 数据库连接池的功能

数据库连接池不仅仅是一个数据库连接的容器,还应具有更加智能的管理数据库连接的功能。我们实现的数据库连接具有以下功能:

  1. 通过getConnection()获取一个数据库连接,如果池中存在连接则直接返回,如果池中没有连接,则新创建一个数据库连接并返回。
  2. 获取的数据库连接进行close()操作的时候,如果连接池中连接数量小于capacity,则该连接自动返回到连接池中,否则直接释放以节省资源。
  3. 即使连接池中的连接数量没有超过capacity,但当其中的部分连接很长时间没有使用时,也要进行释放以节约资源。

3. 数据库连接池中容器的设计

连接池中数据库连接存放的方式可以用队列存放,先放进来的先取出来,也可以用栈来存放,先放进来的后取出来,具体用那种方式,要看需要实现的功能

根据要实现的第三种功能得出,我们需要在存放数据库连接的时候记录连接的上一次使用时间,如果上一次使用时间超过一定时间,则关闭此连接。

如果我们使用队列来存储连接,我们会发现每次新放入的连接都放到了队尾,每次取出来的都是队列前面的最老的数据库连接,所以在不断的存取的过程中,队列里面每一个连接的上一次使用时间都会不断刷新。

一个使用队列的连接池如果有十个连接,每隔10秒取出一个连接使用并放回,则连接池的连接队列会不断从头部取出来,刷新使用时间,放回队列尾部,这样队列每一个连接的上次使用时间都不会超过当前10秒,队列里面所有连接都不能因为超时被释放。

所以我们应当使用栈来存放数据库连接,每次都从上面取出连接,使用完也放回上面。假如使用的频率特别低会导致栈底部的连接长时间未使用,则可以直接释放以节省资源。

连接容器中超时连接的释放有两种方式,1、在往容器中添加或者取出连接的时候释放,2、单独开一个线程不断轮询所有连接释放超时的连接。

我们采用的是第一种方式,在往容器中添加连接的时候释放超时连接,有以下三个原因:

  1. 单独开一个线程需要耗费更多的资源,也更加难以管理
  2. 使用栈来存储连接的话,实际上在不断的存取过程中,栈一直保持着从顶部到底部上次使用时间越来越长的规律,即栈中连接的使用时间是有序的。所以每次释放的时候,只需要从底部向上开始扫描,遇到超时的连接则进行释放,遇上非超时的连接则停止扫描,如果栈中连接均未超时,则只需要扫描最后一个就可以了。
  3. 使用数据库连接的时候,取出来使用应当比放回去的优先级更高,所以释放超时连接的操作应当放在放回连接池中的部分。
  4. 这种方法最坏的情况为:程序开始运行时打开了若干个数据库连接,放置回连接池中,后面则不再进行任何数据库操作(即不再往连接池中取出或存放连接)。这样会导致之前建立的连接一直存放在连接池中,得不到超时释放。但是这种情况出现的几率较少,严格来说这种情况可以通过程序编写避免,所以为了简单和稳定性可以忽略这种情况。

4.数据库连接池的实现

数据库连接池中栈容器的实现是基于Java自带的双向链表来实现的。

public class ConnectionStack{ 
   
    private final int timeout;//单位:秒
    private final int capacity;
    private LinkedList<ConnWithTime> list=new LinkedList<>();
    //内部类,用来记录Connection的上次使用时间
    private class ConnWithTime{ 
   
        private final long time=new Date().getTime();//实例化的时间即为上次归还的时间
        private Connection conn;
        public ConnWithTime(Connection conn){
            this.conn=conn;
        }
        //当前连接是否已经超时
        boolean isTimeout(){
            return new Date().getTime()-time>timeout*1000;
        }
    }
    public ConnectionStack(int capacity,int timeout){
        this.capacity=capacity;
        this.timeout=timeout;
    }
    public synchronized void push(Connection conn){
        clearTimeoutConnection();
        if(list.size()<capacity){
            list.addFirst(new ConnWithTime(conn));
        }else{
            connection.close();
        }
    }
    private void clearTimeoutConnection(){
        //接下来从栈底部开始遍历,把所有超时的连接关闭并且丢弃,直到未超时的连接为止
        //ps:在正常情况下,栈底部最后一个也是活跃的,所以直接break了,平常调用应该是常数时间
        Iterator<ConnWithTime> iterator = list.descendingIterator();
        while (iterator.hasNext()) {
            ConnWithTime connWithTime = iterator.next();
            if (connWithTime.isTimeout()) {
                connWithTime.conn.close();
                iterator.remove();
            } else {
                break;
            }
        }
    }
    public synchronized Connection pop(){
        if(list.isEmpty())return null;
        return list.removeFirst().conn;
    }

    public synchronized void close(){
        Iterator<ConnWithTime> iterator = list.descendingIterator();
        while (iterator.hasNext()) {
            ConnWithTime connWithTime = iterator.next();
            connWithTime.conn.close();
            iterator.remove();
        }
}

数据库连接池的主体部分实现应当如下:

public class DatabasePool{ 
   
    private ConnectionStack stack;
    private final String url,username,password;
    private volatile boolean isRun=true;

    public DatabasePool(String url, String username, String password, int capacity, int timeout) {
       this.url = url;
       this.username = username;
       this.password = password;
       stack = new ConnectionStack(capacity, timeout);
    }

    public Connection getConnection() throws SQLException{
        if(!isRun)throw new SQLException("pool closed");
        Connection conn=stack.pop();
        if(conn==null)conn= DriverManager.getConnection(url, username, password);
        final myConn=conn;
        //使用动态代理,当调用Connection.close()的时候自动将连接放回连接池中
        return (Connection) Proxy.newProxyInstance(
                    DatabasePool.class.getClassLoader(),
                    conn.getClass().getInterfaces(),
                    (proxy, method, args) -> {
                        if (method.getName().equals("close") && isRun) {
  
  //当为调用close函数并且连接池正在运行,则将连接放入stack中,否则直接调用相关函数
                            stack.push(myConn);//
                        } else {
                            return method.invoke(myConn, args);
                        }
                        return null;
                    });
    }
    public void close(){
        isRun=false;
        stack.close();
    }
}

5. 数据库连接池的使用

数据库连接池是线程安全的,所以即使在多线程条件下可以直接使用

DatabasePool pool=new DatabasePool(url,username,password,10,60*60);
//多个线程下可以同时如下使用
Connection conn=null;
try{
    conn=pool.getConnection();
    ...//执行数据库操作 
}catch(SQLException e){
    logger.exception(e);
}finally{
    if(conn!=null)conn.close();
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/194119.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • java多线程面试题大全_java多线程面试题_线程并发面试题

    java多线程面试题大全_java多线程面试题_线程并发面试题1、什么是线程?线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒。2、线程和进程有什么区别?线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一…

    2022年7月7日
    20
  • 微信定位精灵服务器或网络异常,为什么微信定位精灵定位不了怎么办?

    微信定位精灵服务器或网络异常,为什么微信定位精灵定位不了怎么办?方法如下:1、下载“微信定位精灵”软件,安装;2、按图示设置如下,然后缩小地图,把光标定位在大马或任意地方,点击左上角的圆形定位按键完成定位,点右上角的菜单栏,选择“启动微信”,接下来的正常操作就行了。3、打开手机网络,关掉手机的网络定位,GPS等等。打开精灵,看见地图中间有个十字架,那就是你将要定位的地方,比如你的朋友身边。5.点击左上角的定位按钮。一秒你就穿越了。6、打开右边的启动微信。找身边…

    2022年5月7日
    96
  • VS2010中dumpbin工具的使用

    VS2010中dumpbin工具的使用用VS2010生成的.obj文件、.lib库、.dll库、.exe执行文件,如果想查看其中这些文件或库包含了哪些函数以及相关的信息(符号清单),可以通过VS2010自带的dumpbin工具来完成。dumpbin.exe为MicrosoftCOFF二进制文件转换器,它显示有关通用对象文件格式(COFF)二进制文件的信息。可用使用dumpbin检查COFF对象文件、标准COFF对象库、可执行文…

    2022年6月19日
    38
  • 为什么领导不喜欢提拔老实人?退休的领导说出了实话

    为什么领导不喜欢提拔老实人?退休的领导说出了实话

    2022年2月14日
    51
  • Adaboost算法原理分析和实例+代码(简明易懂)

    Adaboost算法原理分析和实例+代码(简明易懂)Adaboost算法原理分析和实例+代码(简明易懂)【尊重原创,转载请注明出处】http://blog.csdn.net/guyuealian/article/details/70995333本人最初了解AdaBoost算法着实是花了几天时间,才明白他的基本原理。也许是自己能力有限吧,很多资料也是看得懵懵懂懂。网上找了一下关于Adaboost算法原理分析,大都是你复制我,我摘…

    2022年6月19日
    28
  • pygame安装(2020版超详细)[通俗易懂]

    pygame安装(2020版超详细)[通俗易懂]在网上找了很多安装教程但是没有成功,最后结合很多教程的步骤终于安装成功啦下面分享一下步骤:1.查看安装版本输入d:(因为我的python是安装在d盘的)输入python会出现安装的版本

    2022年5月10日
    55

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号