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

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


1、模擬實現一個數據庫鏈接接口
編程

//類說明:空實現一個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;
   }
   //由於重點不在這裏,因此這裏省略其它接口...
 }


2、實現數據庫等待超時鏈接池的核心方法多線程


//類說明:鏈接池的實現
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;
        }
    }
}


3、多線程併發模式下對鏈接池的訪問併發


//類說明:數據庫鏈接池測試類
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();
        }
    }
}


4、測試結果報告app

image.png


5、結束語ide

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

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

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

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

相關文章
相關標籤/搜索