在瞭解到wait、notify的使用方式以後,咱們使用wait、notify來實現一個鏈接池。若是還有不清楚wait、notify使用的,請進入傳送門:http://www.javashuo.com/article/p-rokuvpbq-cw.htmljava
首先咱們先來了解下鏈接池的基本原理segmentfault
首先咱們有一個空實現的鏈接類Connection函數
@Data static class Connection { private String connectionName; public Connection(String connectionName) { this.connectionName = connectionName; } }
此處只爲了測試,該類只有一個名字屬性。另外使用了lombok來自動生成set、get方法
接下來就是鏈接池的基本實現測試
@Data public class ConnectionPoolOfWaitNotify { private Integer capacity = 4;//鏈接池中的鏈接數 LinkedList<Connection> linkedList = new LinkedList<>(); //鏈接容器 public ConnectionPoolOfWaitNotify() { IntStream.rangeClosed(1, capacity).forEach(i -> {//構造方法中初始化全部鏈接 linkedList.addLast(new Connection("connection-" + i)); }); } //獲取鏈接 public Connection getConnectioin(long time) throws InterruptedException { synchronized (linkedList) { if (!linkedList.isEmpty()) {//若是存在,拿走第一個 return linkedList.removeFirst(); } if (time <= 0) {//等到拿到鏈接再返回 while (linkedList.isEmpty()) { linkedList.wait(); } return linkedList.removeFirst(); } long lastTime = System.currentTimeMillis() + time; long sleepTime = time; while (linkedList.isEmpty() && sleepTime > 0) { linkedList.wait(sleepTime); sleepTime = lastTime - System.currentTimeMillis(); } if (!linkedList.isEmpty()) { return linkedList.removeFirst(); } else { return null; } } } //歸還鏈接 public void revertConnection(Connection connection) { synchronized (linkedList) { linkedList.addLast(connection); linkedList.notifyAll();//歸還鏈接後通知其餘拿鏈接的線程 } } }
鏈接池中,主要有兩個方法。一個是獲取鏈接(可控制超時時間,超時時間小於等於0則永不超時),一個是歸還鏈接。
主要的邏輯在獲取鏈接的方法裏面,當獲取不到鏈接時,使用wait()方法來使得當前獲取鏈接的線程進入等待狀態。而後在歸還鏈接成功以後,調用notifyAll()方法通知全部正在等待的線程能夠繼續獲取鏈接了,可是繼續獲取鏈接時,還必須繼續搶奪鎖,只有佔鎖成功的線程,才能繼續執行獲取鏈接操做。this
以後執行如下測試代碼spa
public static void main(String[] args) { int allNum = 100; AtomicInteger successNum = new AtomicInteger(0); ConnectionPoolOfWaitNotify connectionPoolOfWaitNotify = new ConnectionPoolOfWaitNotify(); IntStream.rangeClosed(1, allNum).parallel().forEach(i -> { Connection connection = null; try { connection = connectionPoolOfWaitNotify.getConnectioin(100); if (null != connection) { successNum.addAndGet(1); System.out.println("線程" + i + "拿到鏈接" + connection.getConnectionName()); Thread.sleep(200); } else { System.out.println("線程" + i + "沒有拿到鏈接"); } } catch (Exception e) { e.printStackTrace(); } finally { if (null != connection) { connectionPoolOfWaitNotify.revertConnection(connection); } } }); System.out.println("總共拿鏈接次數:" + allNum + ",拿到鏈接次數:" + successNum.get()); }
allNum爲總共獲取鏈接次數,successNum當中記錄獲取成功的次數。每次拿鏈接的超時時間爲100毫秒,拿到鏈接後200的休眠(模擬業務處理)以後歸還鏈接。並且在構造函數中直接初始化了全部的鏈接(讀者能夠考慮下如何作到按需來初始化的話)。運行後結果以下線程
線程23拿到鏈接connection-1 線程69沒有拿到鏈接 線程10拿到鏈接connection-2 線程74拿到鏈接connection-4 線程25拿到鏈接connection-3 線程6沒有拿到鏈接 線程70沒有拿到鏈接 線程72沒有拿到鏈接 線程71沒有拿到鏈接 線程73沒有拿到鏈接 線程75拿到鏈接connection-1 總共拿鏈接次數:100,拿到鏈接次數:25
100次只能拿到25次,那咱們若是設置永不超時呢?調用方式以下,修改超時時間爲0便可code
connection = connectionPoolOfWaitNotify.getConnectioin(0);
以後再次運行結果以下圖片
線程70拿到鏈接connection-1 線程58拿到鏈接connection-2 線程16拿到鏈接connection-3 線程60拿到鏈接connection-4 線程71拿到鏈接connection-1 線程59拿到鏈接connection-4 線程67拿到鏈接connection-3 線程68拿到鏈接connection-2 總共拿鏈接次數:100,拿到鏈接次數:100
OK,所有拿到,沒毛病ci