测试自定义JDBC连接池

       最近开发一个项目会使用MySQL,SqlServer,Oracle,Hive,SparkSql等多类型数据源,通过JDBC形式连接数据源操作数据库。初始频繁的创建JDBC连接和关闭发现非常消耗资源,并且连接hive2和SparkSql时耗时比较长,基于此类需求发现是否可以自定JDBC连接池,实现一次连接反复使用。

方法一:

下面是基于JDBC连接写的一个多线程测试类,该测试方法主要是实现初始化一组数据,线程访问到数据后将数据输出并再返回数据列表中,基本可以实现JDBC连接池功能:

public class Demo {
    
    public static void main(String[] args) {
        for (int j = 0; j < 2; j++) {
            Test test = new Demo().new Test();
            ExecutorService executorService = Executors.newFixedThreadPool(20);
            for(int i = 0; i < 20; i ++){
                executorService.submit(getThread(test));
            }
            executorService.shutdown();
        }
    }
    
    private static Thread getThread(Test test) {
         Thread thr = new Thread(new Runnable() {
            
            @Override
            public void run() {
                try {
                    String str = test.getStr("1");
                    System.out.println( Thread.currentThread().getName() + "\t" + str);
                    Thread.sleep(100);
                    LinkedList linkedList = test.map.get("1");
                    linkedList.add(str);
                    test.map.put("1", linkedList);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                
            }
        });
         return thr;
    }
    
    class Test {
        
        ConcurrentHashMap> map = new ConcurrentHashMap<>();
        
        public String getStr(String key) throws Exception {
            String str = "";
            synchronized (key) {
                LinkedList linkedList = map.get(key);
                if (linkedList == null) {
                    linkedList = new LinkedList<>();
                    for (int i = 0; i < 10; i++) {
                        linkedList.add(i + "");
                    }
                    map.put(key, linkedList);
                    str = linkedList.remove(0);
                }else /*if (linkedList.isEmpty())*/ {
                    while(true) {
                        // 等候10s再看是否有归还连接
                        System.out.println("----------------------");
                        Thread.sleep(10);
                        if (!map.get(key).isEmpty()) {
                            str = map.get(key).remove(0);
                            break;
                        }
                    }
                }/*else {
                    str = linkedList.remove(0);
                } */

            }
            return str;
        }
        
    }
}

代码中标注红色部分放开后会报错,报错原因是多线程访问时,链表有数据在其他线程还未消费,但是自己本线程消费时,链表数据已经清空,因此会报空指针异常。注释后代码不会报错,但是会影响代码性能,链表中有数据依旧需要等候睡眠时间间隔才能获得数据。

方法二:

使用非阻塞线程安全队列ConcurrentLinkedQueue来实现:

import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Logger;

import javax.sql.DataSource;

public class MyDataSource implements DataSource {

    
    // 声明连接池变量,由于操作连接池主要是增加,删除操作,
    // LinkedList比ArrayList增删快,所以这里选用LinkedList
    private ConcurrentLinkedQueue pools = new ConcurrentLinkedQueue();

    public MyDataSource() throws SQLException {
        for (int i = 0; i < 10; i++) {
            Connection conn = PoolConnection.getConn();
            pools.add(conn);
        }
        System.out.println("连接池初始化:" + pools.size());
    }

    @Override
    public Connection getConnection() throws SQLException {
           //如果集合中没有数据库连接对象了,且创建的数据库连接对象没有达到最大连接数量,可以再创建一组数据库连接对象以备使用
        if (!pools.isEmpty()) {
            //从pools集合中取出一个数据库链接对象Connection使用
            final Connection conn1 = pools.poll();
            return (Connection) Proxy.newProxyInstance(MyDataSource.class.getClassLoader(),
                  new Class[] { Connection.class }, new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            if (!method.getName().equalsIgnoreCase("close")) {
                                return method.invoke(conn1, args);
                            } else {
                                pools.add(conn1);
                                return null;
                            }
                        }
                    });
        } else {
            // 如果jdbc数据源连接全部用完,等待有连接
            while(true) {
                // 等候10s再看是否有归还连接
                try {
                    Thread.sleep(10000);
                    return getConnection();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }

    @Override
    public T unwrap(Class iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class iface) throws SQLException {
        return false;
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

}

jdbc连接工具类

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class PoolConnection {

    /**
     * 获取连接对象
     * @return
     */
    public static  Connection getConn() {
        Connection conn = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://ip/dw?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull", "username", "pwd");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return conn;

    }
    /**
     * 释放资源
     * @param conn
     * @param st
     * @param rs
     *
     */
    public static void release(Connection conn, Statement st, ResultSet rs) {
        closeRs(rs);
        closeSt(st);
        closeConn(conn);
    }
    public static void release(Connection conn, Statement st) {
        closeSt(st);
        closeConn(conn);
    }

    private static void closeRs(ResultSet rs) {
        try {
            if (rs != null) {
                rs.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            rs = null;
        }
    }
    private static void closeSt(Statement st) {
        try {
            if (st != null) {
                st.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            st = null;
        }

    }

    private static void closeConn(Connection conn) {
        try {
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            conn = null;
        }

    }

}

测试方法:

import java.sql.Connection;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class Demo {

    public static void main(String[] args) throws Exception {
        MyDataSource ds = new MyDataSource();
        ExecutorService executorService = Executors.newFixedThreadPool(15);
        for(int i = 0; i < 15; i ++){
            executorService.submit(getThread(ds));
        }
        executorService.shutdown();
        
    }
    
    private static Thread getThread(MyDataSource ds) {
         Thread thr = new Thread(new Runnable() {
            
            @Override
            public void run() {
                try {
                    Connection conn = ds.getConnection();
                    if (conn != null) {
                        Thread.sleep(10000);
                        conn.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                
            }
        });
         return thr;
    }
}

比较以上两种方法,最终采用了方法二

 

 

你可能感兴趣的:(java,线程,jdbc,java)