數據庫鏈接池-鏈接的關閉內幕

 咱們常常會遇到這樣那樣的鏈接未關閉的問題,鏈接沒有及時關閉致使的直接後果就是內存泄漏直至down機。咱們也都知道解決的方式,可是在解決了問題以後常常會思考爲何會這樣呢?鏈接close()掉,而後在建立不是很浪費cpu等系統資源嘛?有沒有更好的方法解決呢?你們也常常聽到鏈接池、線程池之類的線程、池的概念,那麼究竟這些概念與咱們的鏈接有什麼關係呢?java

       下面我就想就上面的問題談談個人一點淺見,請你們批評指正。web

       你們都知道java語言是一種語言級的多線程機制的面嚮對象語言。好比說,java的基類object,它就有一些諸如wati(),notify(),notifyall()等線程控制的方法。若是你們學習過操做系統的化,我想應該知道線程存在同步和異步的問題,解決的方法不少,其中就有「信號量機制」實現線程的同步,java的這種同步機制與操做系統大同小異。同步機制是一個比較複雜的問題,若是感興趣能夠找一本操做系統的書看看。數據庫

       下面簡單介紹一些進程和線程的概念:緩存

1.         進程:安全

       進程是資源分配和獨立運行的基本單位。進程的定義不少,下面列舉一些服務器

Ø         進程是程序的一次執行;數據結構

Ø         進程是能夠和別的計算機併發執行的計算;多線程

Ø         進程可定義爲一個數據結構及能在其上進行操做的一個程序併發

Ø         進程是一個程序及其數據在處理機上順序執行時發生的活動;異步

Ø         進程時程序在一個數據集合上的運行過程,時系統進行資源分配和調度的一個獨立單位。

2.         線程

因爲進程是一個資源擁有者,於是在進程的建立、撤消和切換過程當中,系統必須爲之付出較大的時空開銷。也正由於如此,在系統中所設置的進程數目不宜過多,進程切換的頻率也不宜太高,但這也就限制了併發程度的進一步提升。所以便引出了線程的概念

把線程做爲調度和分派的基本單位,而把進程做爲資源擁有的基本單位,使傳統進程的兩個屬性分開,線程便能輕裝運行,從而顯著提升系統的併發程度。

Ø         在同一個進程內能夠有多個線程;

Ø         同一個進程內的線程切換不會引發進程切換;

Ø         一個進程的線程切換到另外一個進程的線程時會引發進程切換

3.         JSP/SERVLET

而咱們經常使用的jsp/ervlet這種j2ee的體系結構正是創建在java的多線程機制之上的。JSP/SERVLET容器會自動使用線程池等技術來支持系統的運行。所以,JSP/SERVLET的實質是一種線程技術,JSP會在運行時被編譯成servlet來運行,如圖所示:


 當客戶端向服務器發出一個請求時,servlet容器會分配一個線程專門處理這個請求,線程內容就是JSP/servlet應用程序。

       這部份內容與本主體無關,只是順便說說。

4.         線程池

       首先介紹一下線程池:

       線程的建立和銷燬,以及切換,執行都是要耗費系統資源的。當系統訪問量比較大的時候,服務器內就會建立太多的線程,直至資源徹底消耗,這對於應用系統的正常運行是有致命傷害的。

       爲了可以在訪問尖峯時限制活動線程的數量,同時減小線程頻繁建立和銷燬帶來的系統開銷,提升系統的大訪問量的處理性能和速度,須要事先建立必定數量的線程供調用者循環反覆使用,這就是「池」技術。

       線程的基本原理是基於隊列queue這種數據結構的,經過不斷查詢隊列queue是否有能夠運行的線程。若是有,就當即運行線程,沒有,則鎖定等待,直到有新的線程加入被解鎖。(這種鎖定機制,就是所謂的「信號量機制」)。

       一種線程池必須解決以下的問題:死鎖、資源不足、併發錯誤、線程泄漏和請求過載。下面咱們具體舉一個成熟的開源線程池的例子來講明線程池的原理:

    PooledExecutor pool=new PooledExecutor(new BoundedBuffer(20),100);

    pool.setMinimumPoolSize(10);//最小線程數爲10

    poole.setKeepAliveTime(-1);//線程一直運行

    上面的語句設置了線程的最大數目爲100,這樣,就能夠保護系統不會由於訪問量增長致使線程數目的無限增長。使用該線程池以下:

       pool.execute(java.lang.Runnable 本身的線程);

       這一句其實是將「本身的線程」加入一個隊列中,而隊列(先進先出FIFO)另外一段正開啓多個線程不斷讀取這個隊列,一旦隊列中有空閒的線程,線程管理器就將讀取並分配線程來運行它。

      

 

 

    public void execute(Runnable command) throws InterruptedException {

       for (;;) { //一直循環

           synchronized (this) {

              if (!shutdown_) { //確保線程池沒有關閉

                  int size = poolSize_; //當前線程池中線程的數目

                  if (size < minimumPoolSize_) { //若是當前線程數目少於線程池最小數目

                     addThread(command);

                     return;

                  }

                  //若是目前線程池中有超過或等於最小數目的線程

                  //分配一個存在的空閒線程來運行commandhandOff是隊列

                  if (handOff_.offer(command, 0)) {

                     return;

                  }

                  //若是不能分配已有的線程來運行command,那麼建立一個新線程

                  if (size < maximumPoolSize_) {

                     addThread(command);

                     return;

                  }

 

              }

           }

           //若是阻塞,則請求幫助

           if (getBlockedExecutionHandler().bolckedAction(command)) {

              return;

           }

       }

    }

       由上面的代碼可見,PooledExecutor線程池的原理是,當執行execute加載一個應用系統的線程時,線程池內部首先檢查當前線程數目是否達到設定的最小數目。若是沒有達到,啓動新線程運行;若是達到了,那麼檢查有無空閒線程可用;若是沒有空閒的,則建立新線程,直到達到最大數目。

       使用線程池的好處是:首先是循環使用,一經建立後,空閒的線程能夠被反覆使用,提升了運行效率;其次有最大數目的限制,保證了系統的安全性。

5.         鏈接池

終於輪到鏈接池了,經過上面的介紹,咱們對線程及線程池都有個一個大體的瞭解。

在正常狀況下,直接使用JDBC調用數據庫能夠知足一個小型系統的要求,可是當系統規模比較大的狀況下,就會出現數據庫的訪問量迅速提高而令服務器不堪重負的現象,於是爲了解決這一性能問題,經常會使用數據庫鏈接池做爲一個緩存的方式解決。

鏈接池相似上面介紹的線程池。

每次數據庫鏈接的創建都須要花費必定的時空費用,而使用鏈接池,能夠事先創建鏈接。當應用程序須要開始使用時,就從鏈接池中獲取一個鏈接使用,應用程序使用完畢,經過close()方法將鏈接歸還鏈接池。講到這裏,我門就沒必要在擔憂close()方法會不會影響性能了,徹底能夠放心大膽的使用。由於,它實際上並無關閉鏈接,而是將鏈接歸還鏈接池,供下次使用。

當併發增長是,鏈接池會不斷的自動建立新的鏈接知足調用,直到達到鏈接池的最大數目;當鏈接池鏈接減小甚至沒有時,鏈接池自動關閉一些鏈接,保持最小數目。

所以鏈接池的使用節省了鏈接創建時間,消除了數據庫頻繁鏈接帶來的開銷和瓶頸。

小提示:不知道你們有沒有注意到配置websphere時有關於鏈接池最大最小數目的配置。呵呵,道理就在這。

那麼,咱們常常面對的鏈接未關閉的問題致使的系統速度很慢的問題就很容易說明了,就是由於線程池已經達到了最大數目,沒有可用的了。因此,其餘操做只有等待的份,等待那些應用用完了,被垃圾回收了,才能釋放出可用的鏈接。

相關文章
相關標籤/搜索