数据库连接池

以前一直在做android,用的是sqlite,最近做的东西需要用到数据库,第一次接触数据库,用jdbc。本来用一个单例一条连接,好像connection是线程安全的,测试了多线程环境下,有一些连接等待了超过一分钟时间,所以就写了一个数据库连接池,其实是跟数据库无关的东西。测试了一下,用2500条线程,会极少数连接耗时会达到3000毫秒,大多数是在100以内,贴上代码,大家看看。东西有借有还,用了SqlConnection一定要还,不然不知道会出什么问题。代码有注释,出bug了大家交流交流。


1、数据库连接池操作类,主要用于获取数据库连接,并且当数据库连接用完了以后的归还方法。同时在这里实例化闲置超时监听。 

package com.allin.servers.sql.pool;

/**
 * 数据库连接池操作类
 * 
 * @author CSC
 * 
 */
public class SqlPoolManager {
	private static SqlPoolManager me;
	private static Object lock = new Object();
	private SqlPool pool;

	/**
	 * 构造函数,在实例化的同时实例化连接池类。
	 */
	private SqlPoolManager() {
		pool = new SqlPool();
		pool.setListener(new Listener());
		pool.init();
	}

	/**
	 * 采用恶汉式单例,唯一的获取连接池操作类实例的方法。
	 * 
	 * 加同步避免多线程调用多次实例化该类。
	 * 
	 * @return
	 */
	public static SqlPoolManager get() {
		if (me == null) {
			synchronized (lock) {
				if (me == null) {
					me = new SqlPoolManager();
				}
			}
		}
		return me;
	}

	/**
	 * 获得数据库连接类。
	 * 
	 * @return
	 */
	public SqlConnection getConnection() {
		SqlConnection conn = pool.getConnection();
		return conn;
	}

	/**
	 * 在用完数据库连接类后将连接类归还方法。
	 * 
	 * @param connection
	 */
	public void returnConnection(SqlConnection connection) {
		pool.returnConnection(connection);
	}

	/**
	 * 私有内部类,不允许其他调用类实例化该类
	 * 
	 * @author uuu
	 * 
	 */
	private class Listener implements SqlConnectTimeoutListener {

		public void connectTimeOut(SqlConnection conn) {
			pool.removeConnection(conn);
		}
	}
}

2、数据库连接池,主要维持了一个用于保持数据库连接的集合,并且控制连接池的连接数量,处理连接类的分发归还规则。

package com.allin.servers.sql.pool;

import java.util.ArrayList;

/**
 * 1、数据库连接池,设立数据库连接缓冲区,优化连接性能。
 * 2、默认最大连接数为100.当连接数未达到100时,每增加一个连接则增加一个connection,默认最小连接10.
 * 3、当达到最大连接请求超过100则为每一条connection分配连接请求。 4、不设超时。
 * 
 * @author CSC
 * 
 */
public class SqlPool {

	Object lock = new Object();

	SqlConnectTimeoutListener listener;

	/**
	 * 记录当前共建立了多少连接。
	 */
	int count;

	/**
	 * 处于计时状态的数量
	 */
	int timingCount;

	/**
	 * 连接池初始容量,该容量影响在数据量少的时候的数据库连接速度,如果设置过大则是系统维护太多连接,并且导致启动慢。
	 */
	private int initialCapacity;

	/**
	 * 连接池最大容量。
	 */
	private int maxCapacity;

	/**
	 * 线程安全的数据库连接维持集合。
	 */
	ArrayList<SqlConnection> connections = new ArrayList<SqlConnection>();

	/**
	 * 创建一个默认初始容量为10,最大容量为100的连接池。
	 */
	public SqlPool() {
		this(100);
	}

	/**
	 * 创建一个默认初始容量为10,最大容量为maxCapacity的连接池。
	 * 
	 * @param maxCapacity
	 *            最大容量。
	 */
	public SqlPool(int maxCapacity) {
		this(10, maxCapacity);
	}

	/**
	 * 创建一个初始容量为initialCapacity,最大容量为maxCapacity的连接池。
	 * 
	 * @param initialCapacity
	 *            初始容量。
	 * @param maxCapacity
	 *            最大容量。
	 */
	public SqlPool(int initialCapacity, int maxCapacity) {
		this.initialCapacity = initialCapacity;
		this.maxCapacity = maxCapacity;
	}

	/**
	 * 获得数据库连接.
	 * 
	 * @return
	 */
	public synchronized SqlConnection getConnection() {

		if (!connections.isEmpty()) {
			SqlConnection conn = getOutConn();
			System.out.println("!isEmpty .. count is : " + count
					+ " size is : " + connections.size());
			return conn;
		}

		if (count < maxCapacity) {
			SqlConnection conn = getNewConn();
			System.out.println("new connetion");
			return conn;
		}

		try {
			while (connections.isEmpty()) {
				wait();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		return getOutConn();
	}

	/**
	 * 取消计时,并将正在计时的数量 -1
	 * 
	 * @param conn
	 */
	private void cancelTimer(SqlConnection conn) {
		boolean isCancel = conn.cancelTimer();
		if (isCancel)
			timingCount--;
	}

	/**
	 * 开始计时,并将正在计时的数量 +1
	 * 
	 * @param conn
	 */
	private void timing(SqlConnection conn) {
		conn.timing();
		System.out.println("timing");
		timingCount++;
	}

	/**
	 * 移除数据库连接.
	 * 
	 * @param connection
	 */
	public synchronized void removeConnection(SqlConnection connection) {
		for (int i = 0; i < connections.size(); i++) {
			if (connection == connections.get(i)) {
				connection.close();
				connections.remove(i);
				count--;
				timingCount--;
				System.out.println("count is : " + count + ". i is : " + i
						+ ". timingCount is : " + timingCount);
				break;
			}
		}
	}

	/**
	 * 获得即将分出去的数据库连接,将最早加入的分出去。
	 * 
	 * @return
	 */
	private SqlConnection getOutConn() {
		SqlConnection conn = connections.remove(0);
		cancelTimer(conn);
		return conn;
	}

	/**
	 * 归还已经分出去的连接,同时将归还的连接启动闲置计时器。
	 * 
	 * 当总创建连接数与正在计时数的差值大于最小连接数开始计时,以保证一共有initialCapacity条连接不被释放。
	 * 
	 * @param conn
	 */
	public synchronized void returnConnection(SqlConnection conn) {
		connections.add(conn);
		if ((count - timingCount) > initialCapacity) {
			timing(conn);
		}
		notifyAll();
	}

	/**
	 * 新建sql连接,在新建的同时将连接数增加1
	 * 
	 * @return
	 */
	private SqlConnection getNewConn() {
		SqlConnection conn = new SqlConnection();
		conn.setListener(listener);
		count++;
		return conn;
	}

	/**
	 * 初始化初始容量的数据库连接。
	 */
	public void init() {
		for (int i = 0; i < initialCapacity; i++) {
			connections.add(getNewConn());
		}
	}

	public void setListener(SqlConnectTimeoutListener listener) {
		this.listener = listener;
	}

}


3、闲置超时监听。
package com.allin.servers.sql.pool;

public interface SqlConnectTimeoutListener {
	public void connectTimeOut(SqlConnection conn);
}
4、数据库连接类,维持了一个数据库连接,主要是用于计算数据库连接的闲置时间。并在超时了通知数据库连接池。
package com.allin.servers.sql.pool;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Timer;
import java.util.TimerTask;
import com.allin.servers.sql.SqlConfig;

public class SqlConnection {

	// 空闲时间计时器
	Timer timer = new Timer();

	// 数据库连接
	Connection conn;

	/**
	 * 用于命名的计数
	 */
	private static int count;

	// 闲置时间超时监听器
	SqlConnectTimeoutListener listener;

	// 闲置超时时间
	private final static long TIME_OUT = 1000 * 10;

	// 计算超时的起始时间
	private long start;

	// 闲置时间
	private long idleTime;

	// 判断时候是处于闲置状态
	private boolean isIdle;

	// 连接名字
	private String name;

	private Task task;

	/**
	 * 数据库连接构造方法,将在初始化数据库连接,调用者可以在该构造方法为连接命名 。
	 * 
	 * @param name
	 */
	public SqlConnection(String name) {
		connect();
		this.name = name;
	}

	/**
	 * 数据库连接构造方法,将在初始化数据库连接,并将以默认的"SqlConnection-"加上一共建立的连接数作为连接名。
	 */
	public SqlConnection() {
		this("SqlConnection-" + count++);
	}

	private void connect() {
		try {
			Class.forName(SqlConfig.jdbcDriverClassName);
			conn = DriverManager.getConnection(SqlConfig.jdbcUrl,
					SqlConfig.jdbcUsername, SqlConfig.jdbcPassword);
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

	}

	/**
	 * 开始计时
	 */
	public void timing() {
		task = (Task) getTask();
		timer.schedule(task, TIME_OUT, TIME_OUT);
		start = System.currentTimeMillis();
		isIdle = true;
	}

	/**
	 * 移除计时
	 */
	public boolean cancelTimer() {
		boolean isCancel = false;

		if (isIdle) {
			task.cancel();
			isCancel = true;
		}
		isIdle = false;
		return isCancel;
	}

	/**
	 * 得到数据库连接闲置时间,当数据库不是处于空闲时返回0
	 * 
	 * @return 闲置时间
	 */
	public long getIdleTime() {
		if (isIdle) {
			idleTime = System.currentTimeMillis() - start;
			return idleTime;
		}
		return 0;
	}

	private TimerTask getTask() {
		return new Task(this);
	}

	protected void timeOut() {
		if (listener != null) {
			listener.connectTimeOut(this);
		}
	}

	/**
	 * 结束数据库连接,释放资源
	 */
	public void close() {
		try {
			conn.close();
			count--;
			task.cancel();
			timer.cancel();
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (NullPointerException e) {
		}
	}

	/**
	 * 得到数据库连接
	 * 
	 * @return
	 */
	public Connection getConn() {
		return conn;
	}

	/**
	 * 设置数据库连接超时监听
	 * 
	 * @param listener
	 */
	public void setListener(SqlConnectTimeoutListener listener) {
		this.listener = listener;
	}

	/**
	 * 得到数据库闲置状态
	 * 
	 * @return
	 */
	public boolean isIdle() {
		return isIdle;
	}

	/**
	 * 得到设置的数据库连接名
	 * 
	 * @return
	 */
	public String getName() {
		return name;
	}

	/**
	 * 设置数据库连接类的名字.
	 * 
	 * @param name
	 */
	public void setName(String name) {
		this.name = name;
	}

}

class Task extends TimerTask {
	SqlConnection conn;

	Task(SqlConnection conn) {
		this.conn = conn;
	}

	@Override
	public void run() {
		conn.timeOut();
	}
}








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值