java併發編程 - 利用對象等待和通知機制實現一個等待超時的連接池

        衆所周知,Java的Object對象提供的,wait()和notify()/notifyAll()等接口是併發編程的重要組成部分。它們對多線程之間的協作起了非常重要的作用,實際開發中也有很多場景可以採用。廢話少說,今天我們就用此機制來模擬實現一個jdbc支持等待超時模式的連接池。


一、模擬實現一個數據庫連接接口

//類說明:空實現一個Connection接口(因爲重點不在這裏,所以以下接口中的方法只做簡單處理)
public class SqlConnectImpl implements Connection{
   
   /*拿一個數據庫連接*/
    public static final Connection fetchConnection(){
        return new SqlConnectImpl();
    }

   @Override
   public boolean isWrapperFor(Class<?> arg0) throws SQLException {
      // TODO Auto-generated method stub
      return false;
   }
   //因爲重點不在這裏,所以這裏省略其它接口...
 }


二、實現數據庫等待超時連接池的核心方法


//類說明:連接池的實現
DBPool {
    //模擬:數據庫連接池
    LinkedList<Connection> pool = LinkedList<Connection>()(initialSize) {
        if(initialSize > ) {
            for(int i = 0;i < initialSize; i++) {
                pool.addLast(SqlConnectImpl.fetchConnection());
            }
        }
    }

    //連接池:釋放連接,通知其他線程
    public void releaseConnection(Connection connection) {
        if (connection != null) {
            synchronized (pool){
                pool.addLast(connection);
                pool.notifyAll();
            }
        }
    }

    //連接池:獲取連接使用
    public Connection fetchConnection(long mills) throws InterruptedException {
        synchronized (pool){
            //未設置超時,直接獲取
            if(mills <0){
                while (pool.isEmpty()){
                    pool.wait();
                }
                return pool.removeFirst();
            }
            //設置超時
            long future = System.currentTimeMillis()+mills;/*超時時刻*/
            long remaining = mills;
            while (pool.isEmpty() && remaining > 0){
                pool.wait(remaining);
                //喚醒一次:重新計算等待時長
                remaining = future - System.currentTimeMillis();
            }
            Connection connection = null;
            if(!pool.isEmpty()){
                connection = pool.removeFirst();
            }
            return connection;
        }
    }
}


三、多線程併發模式下對連接池的訪問


//類說明:數據庫連接池測試類
public class DBPoolTest {
    static DBPool pool  = new DBPool(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
                    Connection connection = pool.fetchConnection(1000);
                    if (connection != null) {
                        try {
                            connection.createStatement();
                            connection.commit();
                        } finally {
                            pool.releaseConnection(connection);
                            got.incrementAndGet();
                        }
                    } else {
                        notGot.incrementAndGet();
                        System.out.println(Thread.currentThread().getName()
                              +"等待超時!");
                    }
                } catch (Exception ex) {
                } finally {
                    count--;
                }
            }
            end.countDown();
        }
    }
}


四、測試結果報告

image.png


五、結束語

總結:1) 對wait()、notify()、notifyAll()等方法使用時須用,synchronized關鍵包裹(對象、方法或塊)均可, 不包裹運行中必報錯;

          2) 線程在執行wait()方法在會自動釋放持有的鎖;

          3) 線程在執行notify()或notifyAll()後,不會立即釋放該線程持有的鎖資源,只有在synchronized包裹的語句塊或方法執行完畢後纔會釋放;

          4) 採用notify()方法喚醒時只會隨機喚醒一個線程,在多喚醒條件下不適用此方法。推薦使用notifyAll()喚醒所有與鎖對象相關的所有線程;



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章