基本原理就是通过wait()和notifiyAll()的一个简单的消费者生产者模型实现。
话不多说先上代码:
SqlConnectImpl类简单的实现了jdk的Connection接口,这里fetchConnection()拿到一个当前对象,也就是SqlConnectImpl。因为是一个demo,所以大部分方法默认空实现。
public class SqlConnectImpl implements Connection{
/*拿一个数据库连接*/
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
}
}
连接池对象,有两个同步方法,因为用到了等待-通知模型,所以在方法进入前需要获得对象锁。
// 等待方遵循如下规则
synchronized(对象) {
while(条件不满足) {
对象.wait();
}
}
// 通知方
synchronized(对象) {
改变条件;
对象.notifiyAll();
}
fetchConnection()和releaseConnection(),用来获取连接和释放连接。连接池通过链表实现。
public class DBPool {
private static final LinkedList<Connection> DB_POOL = new LinkedList<>();
public DBPool(int initialSize) {
if (initialSize > 0) {
for (int i = 0; i < initialSize; i++) {
DB_POOL.addLast(SqlConnectImpl.fetchConnection());
}
}
}
public void releaseConnection(Connection connection) {
synchronized (DB_POOL) {
DB_POOL.addLast(connection);
// 通知wait()状态的线程,有connection被放入连接池了,你们可以去竞争啦
DB_POOL.notifyAll();
}
}
public Connection fetchConnection(long mills) throws InterruptedException {
synchronized (DB_POOL) {
if (mills < 0) {
while (DB_POOL.isEmpty()) {
DB_POOL.wait();
}
return DB_POOL.removeLast();
}
long future = System.currentTimeMillis() + mills;
long timeout = mills;
// 表示当连接池没有连接时,等待timeout秒
while (DB_POOL.isEmpty() && timeout > 0) {
DB_POOL.wait(timeout);
timeout = future - System.currentTimeMillis();
}
if (!DB_POOL.isEmpty()) {
return DB_POOL.removeLast();
}
return null;
}
}
}
DBPoolTest类创建了50个线程,每个线程从连接池中取连接20次,通过线程安全的计数器来记录每个线程成功取到连接的次数和失败的次数。
public class DBPoolTest {
static DBPool pool = new DBPool(20);
/**
* 控制器:控制main线程将会等待所有Worker结束后才能继续执行
*/
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;
}
@Override
public void run() {
while (count > 0) {
try {
// 从线程池中获取连接,如果1000ms内无法获取到,将会返回null
// 分别统计连接获取的数量got和未获取到的数量notGot
Connection connection = pool.fetchConnection(1000);
if (connection != null) {
try {
connection.createStatement();
connection.commit();
} finally {
pool.releaseConnection(connection);
got.incrementAndGet();
}
} else {
notGot.incrementAndGet();
System.out.println(Thread.currentThread().getName()
+"等待超时!");
}
} catch (Exception ex) {
} finally {
count--;
}
}
end.countDown();
}
}
}
思路很简单,只是用到了wait()和notifyAll()。其中wait()方法和notifyAll()都需要获得对象锁,否则程序运行会报错,区别是wait()方法会使当前线程等待并释放掉对象锁,为什么会释放掉对象锁呢?因为如果不释放掉对象锁的话,notifyAll()也需要对象锁,那这样岂不是会造成死锁?而notifyAll()方法在跑完整个同步代码块或者同步方法之后才会释放掉锁。
虽然简单的实现了连接池,但是使用了synchronized,这样肯定会拖累程序的运行效率,所以还可以接着优化,下期见分晓哦❤️