連接池小實例

工作之餘看了一下Java的等待通知機制原理,然后里面有一個資料庫連接池的小Demo,覺得蠻有意思。所以看完之後,自己動手寫了一個簡單的實例,程式碼雖然簡單,但是對於理解連接池還是小有幫助。話不多說,直接上程式碼。
鏈接佔位:後面有時間了再來總結Java的等待通知機制


連接定義

public class Connection {

    public void execute() {
        try {
            Thread.sleep((long) (Math.random() * 10));
        } catch (InterruptedException e) {
        }
    }
}

連接驅動定義

public class ConnectionDriver {

    public static Connection createConnection() {
        return new Connection();
    }
}

連接池定義

public class ConnectionPool {

    private final LinkedList<Connection> pool = new LinkedList<>();

    public ConnectionPool(int count) {
        if (count <= 0) {
            count = 1;
        }
        for (int i = 0;i < count;i++) {
            pool.addLast(ConnectionDriver.createConnection());
        }
    }

    public Connection fetchConnection() throws InterruptedException {
        return fetchConnection(-1);
    }

    public Connection fetchConnection(int mil) throws InterruptedException {
        synchronized (pool) {
            if (mil <= 0) {
                while (pool.isEmpty()) {
                    pool.wait();
                }
                return pool.removeFirst();
            } else {
                long fuTime = System.currentTimeMillis() + mil;
                long remain = mil;
                while (pool.isEmpty() && remain > 0) {
                    pool.wait(remain);
                    remain = fuTime - System.currentTimeMillis();
                }
                Connection result = null;
                if (!pool.isEmpty()) {
                    result = pool.removeFirst();
                }
                return result;
            }
        }
    }

    public void releaseConnection(Connection connection) {
        if (connection == null) {
            return;
        }
        synchronized (pool) {
            pool.addLast(connection);
            pool.notifyAll();
        }
    }
}

連接池測試

public class ConnectionPollCeshi {

    public static void main(String[] args) throws InterruptedException {
        int threadCount = 10;
        ConnectionPool pool = new ConnectionPool(5);

        CyclicBarrier start = new CyclicBarrier(1);
        CountDownLatch end = new CountDownLatch(threadCount);
        AtomicInteger gotSuccess = new AtomicInteger();
        AtomicInteger gotFailed = new AtomicInteger();

        for (int i = 0;i < threadCount;i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        start.await();
                    } catch (Exception e) { }

                    for (int i = 0;i < 10;i++) {
                        try {
                            Connection connection = pool.fetchConnection(2);
                            if (connection == null) {
                                gotFailed.incrementAndGet();
                            } else {
                                connection.execute();
                                pool.releaseConnection(connection);
                                gotSuccess.incrementAndGet();
                            }
                        } catch (InterruptedException e) {
                            gotFailed.incrementAndGet();
                        }
                    }

                    end.countDown();
                }
            }).start();
        }

        end.await();
        System.out.println("獲取成功: " + gotSuccess.get());
        System.out.println("獲取失敗: " + gotFailed.get());
    }
}

運行結果

獲取成功: 48
獲取失敗: 5

實現原理

上面使用了經典的等待通知機制,分為等待角色和通知角色,如下:

等待角色偽程式碼

{
  while(條件不變) {
    obj.wait();
  }
}

通知角色偽程式碼

{
  改變條件
  obj.notifyAll();
}

對於Java等待通知裡面的等待隊列、同步隊列還有很多值得梳理的地方,後面有時間後來認真總結,今天划水就先划到這裡。