我們經(jīng)常在使用數(shù)據(jù)庫(kù)連接池時(shí)會(huì)遇到如網(wǎng)絡(luò)不好連接池報(bào)等待超時(shí)異常邮绿,總是感覺(jué)別人寫(xiě)的框架很牛逼,在看了上一節(jié)的等待通知機(jī)制之后镀层,我們也可以自己動(dòng)手寫(xiě)一個(gè)連接池镰禾,并且如果超出一定的時(shí)間我們可以對(duì)用戶提示異常。
以下是對(duì)該連接池的具體實(shí)現(xiàn)代碼:
public class ConnectionPoolTest {
static ConnectionPool pool = new ConnectionPool(10);
// 保證所有ConnectionRunner能夠同時(shí)開(kāi)始
static CountDownLatch start = new CountDownLatch(1);
// main線程將會(huì)等待所有ConnectionRunner結(jié)束后才能繼續(xù)執(zhí)行
static CountDownLatch end;
// 超時(shí)等待時(shí)間唱逢,可以修改等待時(shí)間進(jìn)行觀察
static long timeoutMills = 2000;
public static void main(String[] args) throws InterruptedException {
// 線程數(shù)量吴侦,可以修改線程數(shù)量進(jìn)行觀察
int threadCount = 280;
// 單個(gè)線程獲取連接次數(shù)
end = new CountDownLatch(threadCount);
AtomicInteger got = new AtomicInteger();
AtomicInteger notGot = new AtomicInteger();
for (int i = 0; i < threadCount; i++) {
Thread thread = new Thread(() -> {
try {
start.await();
} catch (Exception ex) {
}
try {
Connection connection = pool.fetchConnection(timeoutMills);
connection.use();
pool.releaseConnection(connection);
got.getAndIncrement();
} catch (Exception ex) {
System.err.println(ex.getMessage());
notGot.getAndIncrement();
}
end.countDown();
}, "ConnectionRunnerThread-" + i);
thread.start();
}
start.countDown();
end.await();
System.out.println("total invoke: " + threadCount);
System.out.println("got connection: " + got);
System.out.println("not got connection " + notGot);
}
}
class ConnectionPool {
private LinkedList<Connection> pool = new LinkedList<>();
public ConnectionPool(int initialSize) {
if (initialSize <= 0) {
throw new IllegalArgumentException("initialSize can not less or equals to zero");
}
for (int i = 0; i < initialSize; i++) {
pool.addLast(new Connection());
}
}
public Connection fetchConnection(long mills) throws InterruptedException, TimeoutException {
synchronized (pool) {
long wakeUp = System.currentTimeMillis() + mills;
long sleepMills = mills;
do {
if (pool.isEmpty()) {
pool.wait(sleepMills);
}
sleepMills = wakeUp - System.currentTimeMillis();
if (sleepMills <= 0) {
throw new TimeoutException("time out, wait for " + mills + " millisecond");
}
} while (pool.isEmpty());
System.out.println(pool);
// 只要從while循環(huán)中返回,則一定是pool不為空
return pool.removeFirst();
}
}
public void releaseConnection(Connection connection) {
if (connection != null) {
synchronized (pool) {
// 歸還連接之后通知等待在pool上的所有線程坞古,讓其他消費(fèi)者能夠感知連接池中有可用的連接
pool.addLast(connection);
pool.notifyAll();
}
}
}
}
class Connection {
// 模擬數(shù)據(jù)庫(kù)連接所要執(zhí)行的操作消耗的時(shí)間(50-99毫秒)
public void use() {
try {
Thread.sleep((long)new Random().nextInt(50) + 50);
} catch (InterruptedException e) {
}
}
}
程序分析:
連接池ConnectionPool
初始化:
在創(chuàng)建連接池對(duì)象時(shí)必須指定連接池容量备韧,然后初始化該連接池內(nèi)指定數(shù)量的連接對(duì)象,將其添加到連接池隊(duì)列中痪枫。
獲取連接:
在調(diào)用
fetchConnection
方法時(shí)需要先獲取隊(duì)列pool的鎖织堂,然后計(jì)算出如果等待超時(shí)的時(shí)刻,同時(shí)進(jìn)入循環(huán)開(kāi)始判斷隊(duì)列是否為空奶陈,如果不為空則開(kāi)始等待給定的時(shí)間易阳,直到被釋放連接的線程喚醒,或者等待超時(shí)吃粒。如果是被喚醒的情況潦俺,剩余的休眠時(shí)間大于0,并且此時(shí)如果隊(duì)列不為空徐勃,跳出循環(huán)并且返回隊(duì)列中的連接對(duì)象事示。如果是超時(shí)的情況,則剩余的休眠時(shí)間小于或等于0疏旨,此時(shí)拋出等待超時(shí)異常很魂。
釋放連接:
調(diào)用
releaseConnection
并且先獲取pool對(duì)象的鎖,添加到隊(duì)列尾部再喚醒等待在pool對(duì)象上的獲取連接的線程
流程圖如下所示: