使用wait和notifyAll来实现一个简单的数据库连接池
我们先通过实现Connection接口来模拟我们的sql连接
public class SqlConnectImpl implements Connection{
//自己定义的方法,用来返回这个sql连接,其他的方法均为覆盖
public static final Connection fetchConnection(){
return new SqlConnectImpl();
}
@Override
public boolean isWrapperFor(Class<?> arg0) throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public <T> T unwrap(Class<T> arg0) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void abort(Executor arg0) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void clearWarnings() throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void close() throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void commit() throws SQLException {
SleepTools.ms(70);
}
@Override
public Array createArrayOf(String arg0, Object[] arg1) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Blob createBlob() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Clob createClob() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public NClob createNClob() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public SQLXML createSQLXML() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Statement createStatement() throws SQLException {
SleepTools.ms(1);
return null;
}
@Override
public Statement createStatement(int arg0, int arg1) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Statement createStatement(int arg0, int arg1, int arg2) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Struct createStruct(String arg0, Object[] arg1) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean getAutoCommit() throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public String getCatalog() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Properties getClientInfo() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public String getClientInfo(String arg0) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public int getHoldability() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public DatabaseMetaData getMetaData() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public int getNetworkTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public String getSchema() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public int getTransactionIsolation() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public Map<String, Class<?>> getTypeMap() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public SQLWarning getWarnings() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isClosed() throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isReadOnly() throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isValid(int arg0) throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public String nativeSQL(String arg0) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public CallableStatement prepareCall(String arg0) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public CallableStatement prepareCall(String arg0, int arg1, int arg2) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public CallableStatement prepareCall(String arg0, int arg1, int arg2, int arg3) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public PreparedStatement prepareStatement(String arg0) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public PreparedStatement prepareStatement(String arg0, int arg1) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public PreparedStatement prepareStatement(String arg0, int[] arg1) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public PreparedStatement prepareStatement(String arg0, String[] arg1) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public PreparedStatement prepareStatement(String arg0, int arg1, int arg2) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public PreparedStatement prepareStatement(String arg0, int arg1, int arg2, int arg3) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void releaseSavepoint(Savepoint arg0) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void rollback() throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void rollback(Savepoint arg0) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setAutoCommit(boolean arg0) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setCatalog(String arg0) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setClientInfo(Properties arg0) throws SQLClientInfoException {
// TODO Auto-generated method stub
}
@Override
public void setClientInfo(String arg0, String arg1) throws SQLClientInfoException {
// TODO Auto-generated method stub
}
@Override
public void setHoldability(int arg0) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setNetworkTimeout(Executor arg0, int arg1) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setReadOnly(boolean arg0) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public Savepoint setSavepoint() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Savepoint setSavepoint(String arg0) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setSchema(String arg0) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setTransactionIsolation(int arg0) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setTypeMap(Map<String, Class<?>> arg0) throws SQLException {
// TODO Auto-generated method stub
}
}
接下来,我们新建一个自己的连接池的类,如下
public class DBPool {
//数据库池的容器,用链表来存储每一个连接,获取连接从头部拿,释放连接向尾部放
private static LinkedList<Connection> pool = new LinkedList<>();
//使用构造方法来初始化数据库池
public DBPool(int initalSize) {
if(initalSize>0) {
for(int i=0;i<initalSize;i++) {
pool.addLast(SqlConnectImpl.fetchConnection());
}
}
}
//在mills时间内还拿不到数据库连接,返回一个null
public Connection fetchConn(long mills) throws InterruptedException {
synchronized (pool) {
//如果mills小于0,表示需要立马拿到连接,但是当链表空的时候,就一直等待,
//需要等待其他释放连接来唤醒,如果拿到连接后,就把链表中该连接删除,知道释放的时候,再加入
if (mills<0) {
while(pool.isEmpty()) {
pool.wait();
}
return pool.removeFirst();
}else {
//当用户输入的mills大于,表示要在mills时间内拿到连接,如果拿不到,则为null
long overtime = System.currentTimeMillis()+mills;
long remain = mills;
while(pool.isEmpty()&&remain>0) {
//当超时了,还未获取到连接时,则返回null,在未超时的时间内,如果拿到连接(当pool不为空时),
//则返回这个连接
pool.wait(remain);
remain = overtime - System.currentTimeMillis();
}
Connection result = null;
if(!pool.isEmpty()) {
result = pool.removeFirst();
}
return result;
}
}
}
//放回数据库连接
public void releaseConn(Connection conn) {
//当连接不再使用的时候,应该释放连接,并且提醒其他要获取连接的线程,我要释放连接了,
//你们等待连接再试试获取连接吧
if(conn!=null) {
synchronized (pool) {
pool.addLast(conn);
pool.notifyAll();
}
}
}
}
我们可以通过测试类来简单测试一下我们的数据库连接池
public class DBPoolTest {
static DBPool pool = new DBPool(10);
// 控制器:控制main线程将会等待所有Woker结束后才能继续执行
static CountDownLatch end;
public static void main(String[] args) throws Exception {
// 线程数量
int threadCount = 50;
end = new CountDownLatch(threadCount);
int count = 20;//每个线程的操作次数
AtomicInteger got = new AtomicInteger();//计数器:统计可以拿到连接的线程
AtomicInteger notGot = new AtomicInteger();//计数器:统计没有拿到连接的线程
for (int i = 0; i < threadCount; i++) {
Thread thread = new Thread(new Worker(count, got, notGot),
"worker_"+i);
thread.start();
}
end.await();// main线程在此处等待
System.out.println("总共尝试了: " + (threadCount * count));
System.out.println("拿到连接的次数: " + got);
System.out.println("没能连接的次数: " + notGot);
}
static class Worker implements Runnable {
int count;
AtomicInteger got;
AtomicInteger notGot;
public Worker(int count, AtomicInteger got,
AtomicInteger notGot) {
this.count = count;
this.got = got;
this.notGot = notGot;
}
public void run() {
while (count > 0) {
try {
// 从线程池中获取连接,如果1000ms内无法获取到,将会返回null
// 分别统计连接获取的数量got和未获取到的数量notGot
Connection connection = pool.fetchConn(1000);
if (connection != null) {
try {
connection.createStatement();
connection.commit();
} finally {
pool.releaseConn(connection);
got.incrementAndGet();
}
} else {
notGot.incrementAndGet();
System.out.println(Thread.currentThread().getName()
+"等待超时!");
}
} catch (Exception ex) {
} finally {
count--;
}
}
end.countDown();
}
}
}
好了,这样我们的连接池就实现完毕了。