1. 程式人生 > >JDBC執行緒池建立與DBCP原始碼閱讀

JDBC執行緒池建立與DBCP原始碼閱讀

建立資料庫連線是一個比較消耗效能的操作,同時在併發量較大的情況下建立過多的連線對伺服器形成巨大的壓力。對於資源的頻繁分配﹑釋放所造成的問題,使用連線池技術是一種比較好的解決方式。

在Java中,連線池已經有很多開源實現了,在這裡使用commons-dbcp2這個包來

建立JDBC連線池

public final class JDBCUtil{
    private static DataSource myDataSource=null;
    private JDBCUtil(){
    }
    static {
        try{
            Properties pro=new Properties();
            InputStream is=JDBCUtil.class.getClassLoader().getResourceAsStream("mysqlPoolConf.properties");
            pro.load(is);
            myDataSource=BasicDataSourceFactory.createDataSource(pro);
        }
        catch(Exception e){
            e.printStackTrace();
        }
    }
    public static Connection getConnection()throws SQLException{
        return myDataSource.getConnection();
    }
    public static void close(Connection conn){
        if(conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

使用很簡單:

(1)    建立DataSource物件例項myDataSource

(2)    通過myDataSource的getConnection()方法獲得資料庫連線並使用

(3)    用完後呼叫連線close()方法來歸還連線。

當然,這人我們不禁心存疑問,呼叫連線close()方法不是不是將連線關閉了嗎?那連接回到連線池又是怎麼實現的?一起來跟著原始碼看看連線池的實現過程:

首先是連線池配置檔案mysqlPoolConf.properties:

為連線池的一些屬性配置,這裡只列舉了一些常用的:

#連線設定
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://172.16.20.242:3306/test?characterEncoding=utf8
username=root
password=123456
#<!-- 初始化連線 -->
initialSize=20
#<!-- 最大空閒連線 -->
maxIdle=20
#<!-- 最小空閒連線 -->
minIdle=5
#最大連線數量
maxActive=100
#是否在自動回收超時連線的時候列印連線的超時錯誤
logAbandoned=true
#是否自動回收超時連線
removeAbandoned=true
#超時時間(以秒數為單位)
#設定超時時間有一個要注意的地方,超時時間=現在的時間-程式中建立Connection的時間,如果 maxActive比較大,比如超過100,那麼removeAbandonedTimeout可以設定長一點比如180,也就是三分鐘無響應的連線進行回收,當然應用的不同設定長度也不同。
removeAbandonedTimeout=180
#<!-- 超時等待時間以毫秒為單位 -->
#maxWait代表當Connection用盡了,多久之後進行回收丟失連線
maxWait=1000

跟隨原始碼看看連線池的實現邏輯:

BasicDataSourceFactory為一個工廠類,在呼叫它的方法createDataSource(Properties properties)時,通過createDataSource()建立了一個PoolingDataSource物件的例項

createDataSource()原始碼如下,我在原始碼中加了一部分註釋:

protected DataSource createDataSource() throws SQLException {
    if (closed) {
        throw new SQLException("Data source is closed");
    }
    // Return the pool if we have already created it
    // This is double-checked locking. This is safe since dataSource is
    // volatile and the code is targeted at Java 5 onwards.
    if (dataSource != null) {//檢查dataSource是否已經關閉
        return dataSource;
    }
    synchronized (this) {//同步程式碼塊,避免多個執行緒來同時建立連線池
        //如果dataSource已經建立,直接返回, 由於同步程式碼塊需要獲得鎖,
            //可能其他執行緒已經建立了dataSource,再釋放鎖,
        //因此,在獲得鎖之後還需要再判斷一下,這一點在平時程式設計中容易忽略。
        if (dataSource != null) {
            return dataSource;
        }
        jmxRegister();//jmx註冊,不影響整體流程
        // create factory which returns raw physical connections
        //呼叫createConnectionFactory()建立JDBC連線工廠driverConnectionFactory,這個工廠使用資料庫驅動來建立最底層的JDBC連線(即物理連線)
        ConnectionFactory driverConnectionFactory = createConnectionFactory();
        // Set up the poolable connection factory
        //呼叫createConnectionPool()建立資料來源使用的連線池,連線池顧名思義就是快取JDBC連線的地方。
        boolean success = false;
        PoolableConnectionFactory poolableConnectionFactory;//連線池工廠,儲存了一系列的狀態資訊 和 JDBC連線工廠
        try {
            poolableConnectionFactory = createPoolableConnectionFactory(driverConnectionFactory);
            poolableConnectionFactory.setPoolStatements(poolPreparedStatements);
            poolableConnectionFactory.setMaxOpenPrepatedStatements(maxOpenPreparedStatements);
            success = true;
        } catch (SQLException se) {
            throw se;
        } catch (RuntimeException rte) {
            throw rte;
        } catch (Exception ex) {
            throw new SQLException("Error creating connection factory", ex);
        }
        if (success) {
            // create a pool for our connections
            //建立 GenericObjectPool<PoolableConnection>類的例項
            //GenericObjectPool是apach-commons-pool包的一個通用物件池類,其建構函式引數是一個物件工廠例項
            createConnectionPool(poolableConnectionFactory);
        }
        // Create the pooling data source to manage connections
        DataSource newDataSource; 
        success = false;
        try {
            newDataSource = createDataSourceInstance();//建立了一個PoolingDataSource的例項
            newDataSource.setLogWriter(logWriter);
            success = true;
        } catch (SQLException se) {
            throw se;
        } catch (RuntimeException rte) {
            throw rte;
        } catch (Exception ex) {
            throw new SQLException("Error creating datasource", ex);
        } finally {
            if (!success) {
                closeConnectionPool();
            }
        }
        // If initialSize > 0, preload the pool
        try {
            for (int i = 0 ; i < initialSize ; i++) {
                connectionPool.addObject();//初始化initialSize個JDBC連線
            }
        } catch (Exception e) {
            closeConnectionPool();
            throw new SQLException("Error preloading the connection pool", e);
        }
        // If timeBetweenEvictionRunsMillis > 0, start the pool's evictor task
        startPoolMaintenance();
        dataSource = newDataSource;
        return dataSource;
    }
}

在這一步建立了一個池化連線類工廠的例項,真正的連線是由這個工廠建立並放入連線池,GenericObjectPool是連線池的具體管理者,它是在commons-pool2這個包中實現的一個通用的物件池,GenericObjectPool負責連線池的管理(包括初始化連線,將多餘的空閒連線關閉等)。之後,在使用需要使用資料庫的Connection的時候,通過PoolingDataSource的getConnection() 方法獲得一個連線,getConnection()方法實現如下:

public Connection getConnection() throws SQLException {
    try {
        //_pool是在BasicDataSource建立的GenericObjectPool<PoolableConnection> connectionPool這個例項
        C conn = _pool.borrowObject();//從JDBC連線池中獲得一個JDBC連線
        if (conn == null) {
            return null;
        }
        //PoolGuardConnectionWrapper是DBCP對JDBC Connection這個類的一個封裝
        return new PoolGuardConnectionWrapper<>(conn);
    } catch(SQLException e) {
        throw e;
    } catch(NoSuchElementException e) {
        throw new SQLException("Cannot get a connection, pool error " + e.getMessage(), e);
    } catch(RuntimeException e) {
        throw e;
    } catch(Exception e) {
        throw new SQLException("Cannot get a connection, general error", e);
    }
}

_pool.borrowObject()得到是PoolableConnection物件的例項,其繼承自DelegatingConnection。

PoolGuardConnectionWrapper繼承了DelegatingConnection這個類且對父類並沒有做太多的改動。DelegatingConnection實現了java.sql.Connection介面,createStatement(),prepareStatement(),commit(),rollback()這些方法和我們以往

通過DriverManager.getConnection()得到的Connection使用方法是一致的。

重點在呼叫colse()方法時,並不是真正的關閉了連線,而是對連線做了狀態清理後將

通過GenericObjectPool的returnObject方法將其歸還至連線池了,看看原始碼就明白了。

先看看DelegatingConnection這個類實現的close方法,主要是做了Statement和ResultSet的清理:

public void close() throws SQLException {
    if (!_closed) {
        closeInternal();
    }
}
protected final void closeInternal() throws SQLException {
    try {
        passivate();
    } finally {
        if (_conn != null) {
            try {
                _conn.close();
            } finally {
                _closed = true;
            }
        } else {
            _closed = true;
        }
    }
}
protected void passivate() throws SQLException {
    // The JDBC spec requires that a Connection close any open
    // Statement's when it is closed.
    // DBCP-288. Not all the traced objects will be statements
    //關閉  Statement and ResultSet
    List<AbandonedTrace> traces = getTrace();
    if(traces != null && traces.size() > 0) {
        Iterator<AbandonedTrace> traceIter = traces.iterator();
        while (traceIter.hasNext()) {
            Object trace = traceIter.next();
            if (trace instanceof Statement) {
                ((Statement) trace).close();
            } else if (trace instanceof ResultSet) {
                // DBCP-265: Need to close the result sets that are
                // generated via DatabaseMetaData
                ((ResultSet) trace).close();
            }
        }
        clearTrace();
    }
    setLastUsed(0);
}

PoolableConnection對close()方法進行了複寫:

/**
 * Returns me to my pool.
 */
 @Override
public synchronized void close() throws SQLException {
    if (isClosedInternal()) {
        // already closed
        return;
    }
    boolean isUnderlyingConectionClosed;
    try {
        isUnderlyingConectionClosed = getDelegateInternal().isClosed();
    } catch (SQLException e) {
        try {
            _pool.invalidateObject(this);
        } catch(IllegalStateException ise) {
            passivate();//在這個方法內部還是呼叫的父類DelegatingConnection的passivate()方法
            getInnermostDelegate().close();
        } catch (Exception ie) {
            // DO NOTHING the original exception will be rethrown
        }
        throw new SQLException("Cannot close connection (isClosed check failed)", e);
    }
     
    if (isUnderlyingConectionClosed) {
        try {
            _pool.invalidateObject(this);
        } catch(IllegalStateException e) {
            passivate();
            getInnermostDelegate().close();
        } catch (Exception e) {
            throw new SQLException("Cannot close connection (invalidating pooled object failed)", e);
        }
    } else {
        try {
            _pool.returnObject(this);
        } catch(IllegalStateException e) {
            passivate();
            getInnermostDelegate().close();
        } catch(SQLException e) {
            throw e;
        } catch(RuntimeException e) {
            throw e;
        } catch(Exception e) {
            throw new SQLException("Cannot close connection (return to pool failed)", e);
        }
    }
}
@Override
protected void passivate()throws SQLException {
    super.passivate();
    setClosedInternal(true);
}

基本邏輯是呼叫父類的passivate()方法做狀態清理,然後將連線歸還給連線池。因此,回到本篇開始的地方,呼叫close()方法是不會關閉該連線的。

最後,DBCP這個包大致的框架結構如下


主要有以下幾點:

1、 實現池化連線物件PoolableConnection,這個即是我們操作資料的連線物件

2、 池化連線物件工廠類PoolableConnectionFactory,池化連線物件的建立工廠

3、 BasicDataSource工廠類BasicDataSourceFactory,用以建立PoolingDataSource類的例項

4、 PoolingDataSource使用PoolableConnectionFactory建立池化連線,並使用GenericObjectPool作為池物件的管理者。

5、 由於一個connection可以對應多個statement,PoolableConnection內部使用KeyedObjectPool來管理這些statement。

個人理解,不足或者錯誤之處敬請指出~~~

相關推薦

JDBC執行建立DBCP原始碼閱讀

建立資料庫連線是一個比較消耗效能的操作,同時在併發量較大的情況下建立過多的連線對伺服器形成巨大的壓力。對於資源的頻繁分配﹑釋放所造成的問題,使用連線池技術是一種比較好的解決方式。在Java中,連線池已經有很多開源實現了,在這裡使用commons-dbcp2這個包來建立JDBC

執行建立執行ThreadPoolExecutor,Executors

                    執行緒的建立與執行緒池及執行緒池工具類 1.執行緒的建立方式 1.1繼承Thread類重寫run方法 public class Test { p

Executor執行原理原始碼解讀

執行緒池為執行緒生命週期的開銷和資源不足問題提供瞭解決方 案。通過對多個任務重用執行緒,執行緒建立的開銷被分攤到了多個任務上。

python執行ThreadPoolExecutor程序ProcessPoolExecutor

1 import time 2 from concurrent.futures import ThreadPoolExecutor 3 4 def get_thread_time(times): 5 time.sleep(times) 6 return times 7 8

Java 執行建立 理解(簡單)(只要反覆理解,就能理解)

import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class cachethreadpool {     public static void main (String

JAVA百科---執行建立工具類

一、前言 在《阿里巴巴Java開發手冊》中提到: 建立執行緒或執行緒池時請指定有意義的執行緒名稱,方便出錯時回溯。 執行緒資源必須通過執行緒池提供,不允許在應用中自行顯式建立執行緒。 執行緒池不允許使用 Executors 去建立,而是通過 ThreadPoolExe

執行建立和多執行等待

在部落格園看到一篇部落格 C# -- 使用執行緒池 ThreadPool 執行多執行緒任務 在這裡使用了執行緒池 雖然也實現了執行緒等待 但是執行緒等待實現的太死板  如果定義未知數量的執行緒池無法實現等待 ManualResetEvent數量已經定死 所

jdk1.7的Java執行架構原理和原始碼解析(ThreadPoolExecutor)

我們現在來看看ThreadPoolExecutor的原始碼是怎麼樣的,也許你剛開始看他的原始碼會很痛苦,因為你不知道作者為什麼是這樣設計的,所以本文就我看到的思想會給你做一個介紹,此時也許你通過知道了一些作者的思想,你也許就知道應該該如何去操作了。 這裡來看下構造方法中對那

CIL鎖,GIL執行的區別,程序執行,同步非同步

一.GIL鎖 什麼是GIL? 全域性直譯器鎖,是加在直譯器上的互斥鎖 GC是python自帶的記憶體管理機制,GC的工作原理:python中的記憶體管理使用的是應用計數,每個數會被加上一個整型的計數器,表示這個資料被引用的次數,當這個整數變為0時則表示該資料已經沒有人使用,成為了垃圾資料,當記憶體佔用達到

C語言多執行基礎-01-執行建立銷燬

一.執行緒建立 首先要關聯標頭檔案Window.h,需要使用Windows系統的底層方法 1.1 執行緒建立演示: //定義一個方法,彈出一個訊息框 //該方法返回值為DWORD WINAPI型別,引數為一個空指標 DWORD WINAPI run(voi

ThreadPoolExecutor執行解析BlockingQueue的三種實現

目的 主要介紹ThreadPoolExecutor的用法,和較淺顯的認識,場景的使用方案等等,比較忙碌,如果有錯誤還請大家指出 ThreadPoolExecutor介紹 ThreadPoolExecutor的完整構造方法的簽名如下 ThreadP

執行的管理,原始碼解析以及RejectedExecution

       對於執行緒池用的也不少,但是最近還是糟了一坑。還是要深入瞭解一下執行緒池的使用,以免以後遭重        對於JavaEE專案來說,服務端專案是一直啟動著,有的時候需要非同步或者大併發的專案來的時候,就會想到使用多執行緒或者執行緒池來維護有關的執行緒。    

執行-建立到銷燬

建立執行緒池 Windows提供了一個執行緒池機制來簡化執行緒池的建立、銷燬以及日常管理。呼叫執行緒池函式時,系統會為程序建立相應的核心資源,其中一些核心資源在程序終止之前都將一直存在。執行緒池的開銷取決於用法:系統會以程序的名義來分配執行緒、其他核心物件以及內部資料結構。 建立一個

JAVA執行ThreadPoolExecutor阻塞佇列BlockingQueue

池技術是典型的享元模式。 頻繁使用new Thread來建立執行緒的方式並不太好。因為每次new Thread新建和銷燬物件效能較差,執行緒缺乏統一管理。好在java提供了執行緒池,它能夠有效的管理、排程執行緒,避免過多的資源消耗。優點如下: 重用

執行學習三:執行ThreadPoolExecutor Executors

/**      * Creates a new {@code ThreadPoolExecutor} with the given initial      * parameters.      *      * @param corePoolSize the

spring執行ThreadPoolTaskExecutor阻塞佇列BlockingQueue

一:  ThreadPoolTaskExecutor是一個spring的執行緒池技術,檢視程式碼可以看到這樣一個欄位: private ThreadPoolExecutor threadPoolExecutor;   可以發現,spring的  ThreadPoolTaskExecutor是

Java執行架構原理和原始碼解析(ThreadPoolExecutor)

在前面介紹JUC的文章中,提到了關於執行緒池Execotors的建立介紹,在文章:《java之JUC系列-外部Tools》中第一部分有詳細的說明,請參閱;文章中其實說明了外部的使用方式,但是沒有說內部是如何實現的,為了加深對實現的理解,在使用中可以放心,我們這裡將做原始碼解析

一心多用多執行-細談java執行submitexecute的區別

深夜學習,發現ThreadPoolExecutor裡面一個小知識點,故開熱點連wifi怒寫submit與execute方法的區別。 1.問題的來源 在看書的時候,涉及到java執行緒池問題的時候常常面臨這樣一個問題。當定義了一個Runnable物件想提交

執行建立使用

#include<pthread.h> #include<stdio.h> void *inc_x(void *x_void_ptr) { int *x_ptr=(in

java執行建立方式

當前環境jdk == 1.8Executors 使用的隱患先來看一段程式碼,我們要建立一個固定執行緒池,假設固定執行緒數是4。程式碼如下:Executors是JAVA併發包中提供的,用來快速建立不同型別的執行緒池。是不是很簡單,建立執行緒池只需一行程式碼。對於一些個人專案或臨