一、前言
今天我們通過wait,notify/notifyAll的知識實現一個等待超時的數據庫連接池
二、代碼
1、自定義一個數據庫連接類
package com.it.test.thread;
public class MyConnction {
public void createStatement() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void commit() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2、創建連接池
package com.it.test.thread;
import java.util.LinkedList;
public class ConnectionPool {
/**
* 數據庫連接池
*/
LinkedList<MyConnction> pools = new LinkedList<>();
/**
* 默認1S的超時時間
*/
private static final int TIME = 100;
/**
* 初始化連接池的
*
* @param size
*/
public ConnectionPool(int size) {
for (int i = 0; i < size; i++) {
pools.addLast(new MyConnction());
}
}
/**
* 從連接池中獲取連接
*
* @return
*/
public MyConnction getPool() {
synchronized (pools) {
long outTime = System.currentTimeMillis() + TIME;
/**
* 如果連接是空的,並且超時時間未結束,則等待休眠
*/
while (pools.isEmpty() && (outTime - System.currentTimeMillis()) > 0) {
try {
//每次被喚醒之後(有可能被其他線程喚醒)
//根據計算剩餘的超時時間繼續休眠
pools.wait(outTime - System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (!pools.isEmpty()) {
return pools.removeFirst();
} else {
return null;
}
}
}
/**
* 釋放連接
*/
public void releasePool(MyConnction connection) {
if (connection != null) {
synchronized (pools) {
pools.addLast(connection);
pools.notifyAll();
}
}
}
}
3、編寫測試類
package com.it.test.thread.pool;
import com.it.test.thread.ConnectionPool;
import com.it.test.thread.MyConnction;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
/**
*類說明:
*/
public class DBPoolTest {
static ConnectionPool pool = new ConnectionPool(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
MyConnction connection = pool.getPool();
if (connection != null)
{
try {
connection.createStatement();
// PreparedStatement preparedStatement
// = connection.prepareStatement("");
// preparedStatement.execute();
connection.commit();
} finally {
pool.releasePool(connection);
got.incrementAndGet();
}
} else {
notGot.incrementAndGet();
System.out.println(Thread.currentThread().getName()
+"等待超時!");
}
} catch (Exception ex) {
} finally {
count--;
}
}
end.countDown();
}
}
}
總共嘗試了: 1000
拿到連接的次數: 652
沒能連接的次數: 348
總結
wait,notify/notifyAll屬於Object下的方法,使用的時候必須在同步代碼塊內,也就是必須在synchronized 關鍵字修飾的代碼內。
- 調用wait方法的時候,會釋放當前的鎖,進入等待狀態
- 調用notify和notifyAll方法,會隨機喚醒或者全部喚醒一個線程,儘量使用notifyAll。因爲notifyAll喚醒的是全部線程。當之前進入wait狀態的線程收到notify喚醒通知之後,重新競爭鎖,如果競爭到了,那麼就在調用wait方法的地方繼續往下執行。
-調用wait方法會釋放當前鎖,而調用notify和notifyAll不會釋放當前鎖,只有執行完notify部分的代碼完,纔會釋放鎖,所以通常我們把調用notify方法方在最後。