Java自学-JDBC 数据库连接池

数据库连接池

线程池类似的,数据库也有一个数据库连接池。 不过他们的实现思路是不一样的。
本章节讲解了自定义数据库连接池类:ConnectionPool,虽然不是很完善和健壮,但是足以帮助大家理解 ConnectionPool 的基本原理。

步骤 1 : 数据库连接池原理 - 传统方式

当有多个线程,每个线程都需要连接数据库执行 SQL 语句的话,那么每个线程都会创建一个连接,并且在使用完毕后,关闭连接。

创建连接和关闭连接的过程也是比较消耗时间的,当多线程并发的时候,系统就会变得很卡顿。

同时,一个数据库同时支持的连接总数也是有限的,如果多线程并发量很大,那么数据库连接的总数就会被消耗光,后续线程发起的数据库连接就会失败。

数据库连接池原理-传统方式
步骤 2 : 数据库连接池原理 - 使用池

与传统方式不同,连接池在使用之前,就会创建好一定数量的连接。
如果有任何线程需要使用连接,那么就从连接池里面借用而不是自己重新创建.
使用完毕后,又把这个连接归还给连接池供下一次或者其他线程使用。
倘若发生多线程并发情况,连接池里的连接被借用光了,那么其他线程就会临时等待,直到有连接被归还回来,再继续使用。
整个过程,这些连接都不会被关闭,而是不断的被循环使用,从而节约了启动和关闭连接的时间。

数据库连接池原理-使用池
步骤 3 : ConnectionPool 构造方法和初始化

  1. ConnectionPool() 构造方法约定了这个连接池一共有多少连接

  2. 在 init() 初始化方法中,创建了 size 条连接。 注意,这里不能使用 try-with-resource 这种自动关闭连接的方式,因为连接恰恰需要保持不关闭状态,供后续循环使用

  3. getConnection, 判断是否为空,如果是空的就 wait 等待,否则就借用一条连接出去

  4. returnConnection, 在使用完毕后,归还这个连接到连接池,并且在归还完毕后,调用 notifyAll,通知那些等待的线程,有新的连接可以借用了。

注:连接池设计用到了多线程的 wait 和 notifyAll

package jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class ConnectionPool {

List&lt;Connection&gt; cs = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span>&lt;Connection&gt;();

<span class="hljs-type">int</span> size;

<span class="hljs-keyword">public</span> <span class="hljs-title function_">ConnectionPool</span><span class="hljs-params">(<span class="hljs-type">int</span> size)</span> {
    <span class="hljs-built_in">this</span>.size = size;
    init();
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">init</span><span class="hljs-params">()</span> {
      
    <span class="hljs-comment">//这里恰恰不能使用try-with-resource的方式,因为这些连接都需要是"活"的,不要被自动关闭了</span>
    <span class="hljs-keyword">try</span> {
        Class.forName(<span class="hljs-string">"com.mysql.jdbc.Driver"</span>);
        <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; size; i++) {
            <span class="hljs-type">Connection</span> <span class="hljs-variable">c</span> <span class="hljs-operator">=</span> DriverManager
                    .getConnection(<span class="hljs-string">"jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8"</span>, <span class="hljs-string">"root"</span>, <span class="hljs-string">"admin"</span>);

            cs.add(c);

        }
    } <span class="hljs-keyword">catch</span> (ClassNotFoundException e) {
        <span class="hljs-comment">// TODO Auto-generated catch block</span>
        e.printStackTrace();
    } <span class="hljs-keyword">catch</span> (SQLException e) {
        <span class="hljs-comment">// TODO Auto-generated catch block</span>
        e.printStackTrace();
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> Connection <span class="hljs-title function_">getConnection</span><span class="hljs-params">()</span> {
    <span class="hljs-keyword">while</span> (cs.isEmpty()) {
        <span class="hljs-keyword">try</span> {
            <span class="hljs-built_in">this</span>.wait();
        } <span class="hljs-keyword">catch</span> (InterruptedException e) {
            <span class="hljs-comment">// TODO Auto-generated catch block</span>
            e.printStackTrace();
        }
    }
    <span class="hljs-type">Connection</span> <span class="hljs-variable">c</span> <span class="hljs-operator">=</span> cs.remove(<span class="hljs-number">0</span>);
    <span class="hljs-keyword">return</span> c;
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">returnConnection</span><span class="hljs-params">(Connection c)</span> {
    cs.add(c);
    <span class="hljs-built_in">this</span>.notifyAll();
}

}

步骤 4 : 测试类

首先初始化一个有 3 条连接的数据库连接池
然后创建 100 个线程,每个线程都会从连接池中借用连接,并且在借用之后,归还连接。 拿到连接之后,执行一个耗时 1 秒的 SQL 语句。

运行程序,就可以观察到如图所示的效果

测试类

package jdbc;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

import jdbc.ConnectionPool;

public class TestConnectionPool {

<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {
    <span class="hljs-type">ConnectionPool</span> <span class="hljs-variable">cp</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ConnectionPool</span>(<span class="hljs-number">3</span>);
    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">100</span>; i++) {
        <span class="hljs-keyword">new</span> <span class="hljs-title class_">WorkingThread</span>(<span class="hljs-string">"working thread"</span> + i, cp).start();
    }

}

}

class WorkingThread extends Thread {
private ConnectionPool cp;

<span class="hljs-keyword">public</span> <span class="hljs-title function_">WorkingThread</span><span class="hljs-params">(String name, ConnectionPool cp)</span> {
    <span class="hljs-built_in">super</span>(name);
    <span class="hljs-built_in">this</span>.cp = cp;
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> {
    <span class="hljs-type">Connection</span> <span class="hljs-variable">c</span> <span class="hljs-operator">=</span> cp.getConnection();
    System.out.println(<span class="hljs-built_in">this</span>.getName()+ <span class="hljs-string">":\t 获取了一根连接,并开始工作"</span>  );
    <span class="hljs-keyword">try</span> (<span class="hljs-type">Statement</span> <span class="hljs-variable">st</span> <span class="hljs-operator">=</span> c.createStatement()){
         
        <span class="hljs-comment">//模拟时耗1秒的数据库SQL语句</span>
        Thread.sleep(<span class="hljs-number">1000</span>);
        st.execute(<span class="hljs-string">"select * from hero"</span>);

    } <span class="hljs-keyword">catch</span> (SQLException | InterruptedException e) {
        <span class="hljs-comment">// TODO Auto-generated catch block</span>
        e.printStackTrace();
    }
    cp.returnConnection(c);
}

}

更多内容,点击了解: JDBC 数据库连接池