wait、notify實戰之實現鏈接池

前言

在瞭解到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

相關文章
相關標籤/搜索