数据库连接池认知+手写一个

首先推荐一篇不错的文章。https://www.cnblogs.com/newpanderking/p/3875749.html

接下来,看了这篇文章之后,我们再看一下

1.首先是连接池的规范

/**
 * 连接池  顶级规范
 */
public interface IPool {
    /**
     * 获取连接
     */
    MyConn getConnection();
    
}
/**
 * 定义了一定规范的抽象类
 */
/**
 * 
 * 连接池 超类:用以提供部分规范
 */
public abstract class BasePool implements IPool{
    
    protected String jdbcDriver;
    
    protected String dbUrl;

    protected String dbUsername;

    protected String dbPassword;
    
    protected int maxConnnect;
    
    protected int initedConnect;
    
    protected List<MyConn> conns;
    
    
    public BasePool(String jdbcDriver, String dbUrl, String dbUsername, String dbPassword, int maxConnnect,int firstInit){ 
        //我本来要放弃这个初始化参数:int firstInit。
        //其本来是决定new多少个connection,来减少启动的消耗。
        //因为第一次优化后产生的bug主要是在于多线程下init<max时,生成了更多的连接导致的问题。
        //但是我看见阿里的druid都有这个参数,
        //就尝试了一下将createConn(int)这个方法加上锁(synchronized)或者lock都可以。
        //这个地方其实应该也可以采用volatile关键字
        //但是其实createConn(int)这个方法应该竞争并没有那么激烈,不用强行在这个地方追求极致的效率
        this.jdbcDriver = jdbcDriver;
        this.dbUrl = dbUrl;
        this.dbUsername = dbUsername;
        this.dbPassword = dbPassword;
        this.maxConnnect = maxConnnect;
        //是为了保证线程安全。但是这样做,效率会降低很多,后续想个方式来处理
        //哈哈 解决了,但是还是把最初的版本放在这里
        //conns=new Vector<>(new MyConn[initedConnect]);
        conns=new ArrayList<>(maxConnnect);
        //创建池
        initPool(firstInit);
        //创建守护线程
        new Thread(new GuardConnection(this,4)).start();;
    }
    
    protected void initPool(int initedConnect) {
        try {
            Class.forName(jdbcDriver);
            createConnections(initedConnect);
        }catch (Exception e) {
            throw new RuntimeException("创建数据库连接出错");
        }
    }

    protected void createConnections(int count) {
        for (int i = 0; i < count; i++) {
            // 大于最大连接数的话就不创建了
            if (initedConnect + 1 > maxConnnect)
                return;
            if (count <= 0)
                throw new RuntimeException("初始化连接数不能为空");
            try {
                Connection conn = DriverManager.getConnection(dbUrl, dbUsername, dbPassword);
                conns.add(new MyConn(conn, false));
                // 已加载的连接数量++
                initedConnect++;
            } catch (SQLException e) {
                e.printStackTrace();
            }
            
        }
    }
    
    public abstract List<MyConn> getConns();
}
/**
 * 连接池实例
 */
public class MyPool extends BasePool {
    
    public MyPool(String jdbcDriver, String dbUrl, String dbUsername, String dbPassword, int maxConnnect, int initedConnect) {
        super(jdbcDriver, dbUrl, dbUsername, dbPassword, maxConnnect, initedConnect);
    }
    
    @Override
    public MyConn getConnection() {
        // 判断是否加载
        if (initedConnect == 0) {
            System.out.println("连接池中还没有连接!");
            throw new RuntimeException("连接池中还没有连接!");
        }
        
        /**
         * (1)当还有可用连接的时候
         * (2)可用连接不够了,需要创建或者等待
         */
        MyConn conn = getActiveConnection();
        // (2)的情况
        while (null == conn) {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //创建连接
            createConnections(1);
            conn = getActiveConnection();
        }
    return conn;
    
    }
    
    /**
     * 从集合中得到连接
     * 
     * @return
     * @return
     */
     private MyConn getActiveConnection() {
        for (int i = 0; i < conns.size(); i++) {
            MyConn conn = conns.get(i);
            if(conn.setBusy(true)) {
                conn.setUseTime(System.currentTimeMillis());
                return conn;
            }
        }
        return null;
    }

    @Override
    public List<MyConn> getConns() {
        return super.conns;
    }
}

2.connection的包装对象

/**
 * 包含conn的对象
 */
public class MyConn {
    /**
     * 实际connection
     */
    private Connection conn;
    /**
     * 是否正在工作
     * 刚创建时,默认是false。
     * volatile关键字,是为了提高效率,将数组的锁定放在了每一个元素的锁定
     * 就有点类似与conCurrentHashMap。
     * 后面发现即使使用了volatile关键字还是会存在问题。
     * 因此后面还是使用了synchronized关键字来控制,但是即使如此,还是讲粒度放在了每个元素上。
     * 从而提高了性能
     */
    private volatile boolean isBusy=false;
    /**
     * 开始使用的时间:用于守护线程去强制回收
     */
    private long useTime;
    /**
     * 查询
     * @param sql
     * @return
     */
    public ResultSet query(String sql) {
        Statement sm = null;
        ResultSet rs = null;
        
        try {
            sm = conn.createStatement();
            rs = sm.executeQuery(sql);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return rs;
    }
    /**
     * 修改操作
     * @param sql
     * @return
     */
    public int update(String sql) {
        Statement sm = null;
        int count = -1;
        try {
            sm = conn.createStatement();
            count = sm.executeUpdate(sql);
        }
        catch (SQLException e) {
            e.printStackTrace();
        }
        return count;
    }
    
    public boolean isBusy() {
        return isBusy;
    }
    /**
     * 当传入的是true的时候,说明,准备占用 
     * 但是外面用的list不安全,因此考虑在set层操作,
     * 这里的返回值是为了确定获取成功
     */
    public boolean setBusy(boolean isBusy) {
        //本来if else这种东西应该抽象出来,但是目前先这样吧
       synchronized(this) {
            if(isBusy) {
                if(this.isBusy) {
                    return !isBusy;
                }
            }
            //false的时候无所谓(后面思考是否会有线程安全的问题)
            this.isBusy=isBusy;
            return isBusy;
        }
    }
    
    public void close() {
        this.isBusy=false;
    }
    
    
    public MyConn(Connection conn, boolean isBusy) {
        super();
        this.conn = conn;
        this.isBusy = isBusy;
    }
    public long getUseTime() {
        return useTime;
    }
    public void setUseTime(long useTime) {
        this.useTime = useTime;
    }
    
    public void rollback() throws SQLException {
        conn.rollback();
    }
}

3.守护线程

/**
 * connection守护线程
 */
public class GuardConnection implements Runnable{
    
    private BasePool pool;
    
    private long time;
    
    @Override
    public void run() {
        while(true) {
            for(int i=0;i<pool.getConns().size();i++) {
                MyConn conn = pool.getConns().get(i);
                if(conn.isBusy() && System.currentTimeMillis() - conn.getUseTime() > time*1000 ) {
                    try {
                        conn.rollback();
                    }catch (Exception e) {
                    }finally {
                        conn.close();
                    }
                }
            }
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
            }
        }
    }
    
        
    public GuardConnection(BasePool pool,long time) {
        super();
        this.pool = pool;
        this.time=time;
    }

}

这样就能够完成一个简单的连接池。但是还有很多东西没有考虑。后面慢慢更新,一步步优化,加上功能:例如connection的守护线程,强制回池,性能等方面的。

---------------------------------------------------------------------------------------------------------------------------------------

目前,强制回池,守护线程都已经做到了,性能方面,经过测试,确实是有所提升。但是还是存在一些不足,例如可供拓展的一些AOP功能等等。后续考虑一下

猜你喜欢

转载自blog.csdn.net/qq_40384690/article/details/82899217