最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java連接池Druid獲取連接getConnection示例詳解

 更新時間:2023年09月15日 11:51:53   作者:福  
這篇文章主要為大家介紹了java連接池Druid獲取連接getConnection示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

Druid連接池

Druid連接池只存儲在connections數(shù)組中,所以獲取連接的邏輯應該比HikariPool簡單一些:直接從connectoins獲取即可。

DruidDataSource.getConnection

直接上代碼:

@Override
    public DruidPooledConnection getConnection() throws SQLException {
        return getConnection(maxWait);
    }

調用了getConnection(maxWait),maxWait是參數(shù)設定的獲取連接的最長等待時間,超過該時長還沒有獲取到連接的話,拋異常。

看getConnection(maxWait)代碼:

public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
        init();
        if (filters.size() > 0) {
            FilterChainImpl filterChain = new FilterChainImpl(this);
            return filterChain.dataSource_connect(this, maxWaitMillis);
        } else {
            return getConnectionDirect(maxWaitMillis);
        }
    }

先調用init,init方法會判斷連接池是否已經(jīng)完成了初始化,如果沒有完成初始化則首先進行初始化,初始化的代碼我們上一篇文章已經(jīng)分析過了。

之后判斷是否有filters,filters的內容我們先放放,暫時不管,直接看沒有filters的情況下,調用getConnectionDirect方法。

getConnectionDirect

方法比較長,我們還是老辦法,分段分析:

public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {
        int notFullTimeoutRetryCnt = 0;
        for (;;) {
            // handle notFullTimeoutRetry
            DruidPooledConnection poolableConnection;
            try {
                poolableConnection = getConnectionInternal(maxWaitMillis);
            } catch (GetConnectionTimeoutException ex) {
                if (notFullTimeoutRetryCnt <= this.notFullTimeoutRetryCount && !isFull()) {
                    notFullTimeoutRetryCnt++;
                    if (LOG.isWarnEnabled()) {
                        LOG.warn("get connection timeout retry : " + notFullTimeoutRetryCnt);
                    }
                    continue;
                }
                throw ex;
            }

上來之后首先無限for循環(huán),目的是從連接池獲取到連接之后,根據(jù)參數(shù)設定可能會做必要的檢查,如果檢查不通過(比如連接不可用、連接已關閉等等)的話循環(huán)重新獲取。

然后調用getConnectionInternal獲取連接,getConnectionInternal方法應該是我們今天文章的主角,我們稍微放一放,為了文章的可讀性,先分析完getConnectionDirect方法。

我們假設通過調用getConnectionInternal方法獲取到一個連接(注意獲取到的連接對象是DruidPooledConnection,不是Connection對象,這個也不難想象,連接池獲取到的連接一定是數(shù)據(jù)庫物理連接的代理對象(或者叫封裝對象,封裝了數(shù)據(jù)庫物理連接Connection對象的對象,這個原理我們在分析HikariPool的時候已經(jīng)說過了。這個DruidPooledConnection對象我們也暫時放一放,后面分析)。

調用getConnectionInternal方法如果返回超時異常,判斷:如果當前連接池沒滿,而且獲取連接超時重試次數(shù)小于參數(shù)notFullTimeoutRetryCount設定的次數(shù)的話,則continue,重新獲取連接。否則,拋出超時異常。

接下來:

if (testOnBorrow) {
                boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
                if (!validate) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("skip not validate connection.");
                    }
                    discardConnection(poolableConnection.holder);
                    continue;
                }
            } else {
                if (poolableConnection.conn.isClosed()) {
                    discardConnection(poolableConnection.holder); // 傳入null,避免重復關閉
                    continue;
                }

testOnBorrow參數(shù)的目的是:獲取連接后是否要做連接可用性測試,如果設定為true的話,調用testConnectionInternal測試連接的可用性,testConnectionInternal方法上一篇文章分析連接回收的時候、處理keepAlive的過程中就碰到過,就是執(zhí)行配置好的sql語句測試連接可用性,如果測試不通過的話則調用discardConnection關閉連接,continue重新獲取連接。

否則,如果testOnBorrow參數(shù)沒有打開的話,檢查當前連接如果已經(jīng)關閉,則調用discardConnection關閉連接(沒太明白連接既然已經(jīng)是關閉狀態(tài),為啥還需要調用?),continue重新獲取連接。

不建議打開testOnBorrow參數(shù),因為連接池都會有連接回收機制,比如上一篇文章講過的Druid的DestroyConnectionThread & DestroyTask,回收參數(shù)配置正常的話,回收機制基本可以確保連接的可用性。打開testOnBorrow參數(shù)會導致每次獲取連接之后都測試連接的可用性,嚴重影響系統(tǒng)性能。

接下來:

if (testWhileIdle) {
                    final DruidConnectionHolder holder = poolableConnection.holder;
                    long currentTimeMillis             = System.currentTimeMillis();
                    long lastActiveTimeMillis          = holder.lastActiveTimeMillis;
                    long lastExecTimeMillis            = holder.lastExecTimeMillis;
                    long lastKeepTimeMillis            = holder.lastKeepTimeMillis;
                    if (checkExecuteTime
                            && lastExecTimeMillis != lastActiveTimeMillis) {
                        lastActiveTimeMillis = lastExecTimeMillis;
                    }
                    if (lastKeepTimeMillis > lastActiveTimeMillis) {
                        lastActiveTimeMillis = lastKeepTimeMillis;
                    }
                    long idleMillis                    = currentTimeMillis - lastActiveTimeMillis;
                    long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis;
                    if (timeBetweenEvictionRunsMillis <= 0) {
                        timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
                    }
                    if (idleMillis >= timeBetweenEvictionRunsMillis
                            || idleMillis < 0 // unexcepted branch
                            ) {
                        boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
                        if (!validate) {
                            if (LOG.isDebugEnabled()) {
                                LOG.debug("skip not validate connection.");
                            }
                            discardConnection(poolableConnection.holder);
                             continue;
                        }
                    }
                }
            }

這段代碼的邏輯是:參數(shù)testWhileIdle設置為true的話,檢查當前鏈接的空閑時長如果大于timeBetweenEvictionRunsMillis(默認60秒)的話,則調用testConnectionInternal測試連接可用性,連接不可用則關閉連接,continue重新獲取連接。

然后:

if (removeAbandoned) {
                StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
                poolableConnection.connectStackTrace = stackTrace;
                poolableConnection.setConnectedTimeNano();
                poolableConnection.traceEnable = true;
                activeConnectionLock.lock();
                try {
                    activeConnections.put(poolableConnection, PRESENT);
                } finally {
                    activeConnectionLock.unlock();
                }
            }

出現(xiàn)了一個removeAbandoned參數(shù),這個參數(shù)的意思是移除被遺棄的連接對象,如果打開的話就把當前連接放到activeConnections中,篇幅有限,這部分內容就不展開了,后面我們會專門寫一篇文章介紹removeAbandoned參數(shù)。

剩下的一小部分代碼,很簡單,根據(jù)參數(shù)設置連接的autoCommit,之后返回連接poolableConnection。

if (!this.defaultAutoCommit) {
                poolableConnection.setAutoCommit(false);
            }
            return poolableConnection;
        }
    }

getConnectionDirect方法源碼分析完成了,下面我們要看一下getConnectionInternal方法,這是真正從連接池中獲取連接的方法。

getConnectionInternal

直接看代碼:

private DruidPooledConnection getConnectionInternal(long maxWait) throws SQLException {
        if (closed) {
            connectErrorCountUpdater.incrementAndGet(this);
            throw new DataSourceClosedException("dataSource already closed at " + new Date(closeTimeMillis));
        }
        if (!enable) {
            connectErrorCountUpdater.incrementAndGet(this);
            if (disableException != null) {
                throw disableException;
            }
            throw new DataSourceDisableException();
        }
        final long nanos = TimeUnit.MILLISECONDS.toNanos(maxWait);
        final int maxWaitThreadCount = this.maxWaitThreadCount;
        DruidConnectionHolder holder;

檢查連接池狀態(tài)如果已經(jīng)disable或cloesed的話,拋異常。

接下來:

for (boolean createDirect = false;;) {
            if (createDirect) {
                createStartNanosUpdater.set(this, System.nanoTime());
                if (creatingCountUpdater.compareAndSet(this, 0, 1)) {
                    PhysicalConnectionInfo pyConnInfo = DruidDataSource.this.createPhysicalConnection();
                    holder = new DruidConnectionHolder(this, pyConnInfo);
                    holder.lastActiveTimeMillis = System.currentTimeMillis();
                    creatingCountUpdater.decrementAndGet(this);
                    directCreateCountUpdater.incrementAndGet(this);
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("conn-direct_create ");
                    }
                    boolean discard = false;
                    lock.lock();
                    try {
                        if (activeCount < maxActive) {
                            activeCount++;
                            holder.active = true;
                            if (activeCount > activePeak) {
                                activePeak = activeCount;
                                activePeakTime = System.currentTimeMillis();
                            }
                            break;
                        } else {
                            discard = true;
                        }
                    } finally {
                        lock.unlock();
                    }
                    if (discard) {
                        JdbcUtils.close(pyConnInfo.getPhysicalConnection());
                    }
                }
            }

初始化createDirect變量為false之后啟動無限循環(huán),意思是不斷循環(huán)直到獲取到連接、或超時等其他異常情況發(fā)生。

緊接著的這段代碼是createDirect=true的情況下執(zhí)行的,createDirect是在下面循環(huán)體中檢查如果:createScheduler不為空、連接池空、活動連接數(shù)小于設定的最大活動連接數(shù)maxActive、并且createScheduler的隊列中排隊等待創(chuàng)建連接的線程大于0的情況下,設置createDirect為true的,以上這些條件如果成立的話,大概率表明createScheduler中的創(chuàng)建線程出問題了、所以createScheduler大概率指望不上了,所以要直接創(chuàng)建連接了。

直接創(chuàng)建的代碼也很容易理解,調用createPhysicalConnection創(chuàng)建物理連接,創(chuàng)建DruidConnectionHolder封裝該物理連接,創(chuàng)建之后獲取鎖資源,檢查activeCount < maxActive則表明創(chuàng)建連接成功、結束for循環(huán),否則,activeCount >= maxActive則說明違反了原則(直接創(chuàng)建連接的過程中createScheduler可能復活了、又創(chuàng)建出來連接放入連接池中了),所以,關閉鎖資源之后,將剛創(chuàng)建出來的連接關閉。

然后:

try {
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                connectErrorCountUpdater.incrementAndGet(this);
                throw new SQLException("interrupt", e);
            }
            try {
                if (maxWaitThreadCount > 0
                        && notEmptyWaitThreadCount >= maxWaitThreadCount) {
                    connectErrorCountUpdater.incrementAndGet(this);
                    throw new SQLException("maxWaitThreadCount " + maxWaitThreadCount + ", current wait Thread count "
                            + lock.getQueueLength());
                }
                if (onFatalError
                        && onFatalErrorMaxActive > 0
                        && activeCount >= onFatalErrorMaxActive) {
                    connectErrorCountUpdater.incrementAndGet(this);
                    StringBuilder errorMsg = new StringBuilder();
                    errorMsg.append("onFatalError, activeCount ")
                            .append(activeCount)
                            .append(", onFatalErrorMaxActive ")
                            .append(onFatalErrorMaxActive);
                    if (lastFatalErrorTimeMillis > 0) {
                        errorMsg.append(", time '")
                                .append(StringUtils.formatDateTime19(
                                        lastFatalErrorTimeMillis, TimeZone.getDefault()))
                                .append("'");
                    }
                    if (lastFatalErrorSql != null) {
                        errorMsg.append(", sql \n")
                                .append(lastFatalErrorSql);
                    }
                    throw new SQLException(
                            errorMsg.toString(), lastFatalError);
                }
                connectCount++;
                if (createScheduler != null
                        && poolingCount == 0
                        && activeCount < maxActive
                        && creatingCountUpdater.get(this) == 0
                        && createScheduler instanceof ScheduledThreadPoolExecutor) {
                    ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) createScheduler;
                    if (executor.getQueue().size() > 0) {
                        createDirect = true;
                        continue;
                    }
                }

獲取鎖資源,檢查等待獲取連接的線程數(shù)如果大于參數(shù)設置的最大等待線程數(shù),拋異常。

檢查并處理異常。

累加connectCount。

之后是上面提到過的對createDirect的處理。

接下來到了最為關鍵的部分,一般情況下createDirect為false,不會直接創(chuàng)建連接,邏輯會走到下面這部分代碼中,從連接池中獲取連接:

if (maxWait > 0) {
                    holder = pollLast(nanos);
                } else {
                    holder = takeLast();
                }
                if (holder != null) {
                    if (holder.discard) {
                        continue;
                    }
                    activeCount++;
                    holder.active = true;
                    if (activeCount > activePeak) {
                        activePeak = activeCount;
                        activePeakTime = System.currentTimeMillis();
                    }
                }
            } catch (InterruptedException e) {
                connectErrorCountUpdater.incrementAndGet(this);
                throw new SQLException(e.getMessage(), e);
            } catch (SQLException e) {
                connectErrorCountUpdater.incrementAndGet(this);
                throw e;
            } finally {
                lock.unlock();
            }

如果參數(shù)設置了maxWait,則調用pollLast限時獲取,否則調用takeLast獲取連接,這兩個方法稍后分析。

之后檢查獲取到的連接已經(jīng)被discard的話,continue重新獲取連接。

釋放鎖資源。

從連接池中獲取到了連接,結束for循環(huán)。

如果takeLast或poolLast返回的DruidConnectionHolder為null的話(調用poolLast超時),處理錯誤信息,拋GetConnectionTimeoutException超時異常(這部分代碼沒有貼出,省略了......感興趣的童鞋自己打開源碼看一下)。

否則,用DruidConnectionHolder封裝創(chuàng)建DruidPooledConnection后返回。

takeLast & pollLast(nanos)

這兩個方法的邏輯其實差不多,主要區(qū)別一個是限時,一個不限時,兩個方法都是在鎖狀態(tài)下執(zhí)行。

具體調用哪一個方法取決于參數(shù)maxWait,默認值為-1,默認情況下會調用takeLast,獲取連接的時候不限時。

建議設置maxWait,否則在特殊情況下如果創(chuàng)建連接失敗、會導致應用層線程掛起,獲取不到任何返回的情況出現(xiàn)。如果設置了maxWait,getConnection方法會調用pollLast(nanos),獲取不到連接后,應用層會得到連接超時的反饋。

先看takeLast方法:

takeLast() throws InterruptedException, SQLException {
        try {
            while (poolingCount == 0) {
                emptySignal(); // send signal to CreateThread create connection
                if (failFast && isFailContinuous()) {
                    throw new DataSourceNotAvailableException(createError);
                }
                notEmptyWaitThreadCount++;
                if (notEmptyWaitThreadCount > notEmptyWaitThreadPeak) {
                    notEmptyWaitThreadPeak = notEmptyWaitThreadCount;
                }
                try {
                    notEmpty.await(); // signal by recycle or creator
                } finally {
                    notEmptyWaitThreadCount--;
                }
                notEmptyWaitCount++;
                if (!enable) {
                    connectErrorCountUpdater.incrementAndGet(this);
                    if (disableException != null) {
                        throw disableException;
                    }
                    throw new DataSourceDisableException();
                }
            }
        } catch (InterruptedException ie) {
            notEmpty.signal(); // propagate to non-interrupted thread
            notEmptySignalCount++;
            throw ie;
        }
        decrementPoolingCount();
        DruidConnectionHolder last = connections[poolingCount];
        connections[poolingCount] = null;
        return last;
    }

如果連接池為空(poolingCount == 0)的話,無限循環(huán)。

調用emptySignal(),通知創(chuàng)建連接線程,有人在等待獲取連接,抓緊時間創(chuàng)建連接。

然后調用notEmpty.await(),等待創(chuàng)建連接線程在完成創(chuàng)建、或者有連接歸還到連接池中后喚醒通知。

如果發(fā)生異常,調用一下notEmpty.signal()通知其他獲取連接的線程,沒準自己沒能獲取成功、其他線程能獲取成功。

下面的代碼,線程池一定不空了。

線程池的線程數(shù)量減1(decrementPoolingCount),然后獲取connections的最后一個元素返回。

pollLast方法的代碼邏輯和takeLast的類似,只不過線程池空的話,當前線程會限時掛起等待,超時仍然不能獲取到連接的話,直接返回null。

Druid連接池獲取連接代碼分析完畢!

小結

Druid連接池的連接獲取過程的源碼分析完畢,后面還有連接歸還過程,更多關于java Druid獲取連接getConnection的資料請關注腳本之家其它相關文章!

相關文章

  • Spring MVC溫故而知新系列教程之從零開始

    Spring MVC溫故而知新系列教程之從零開始

    Spring MVC 框架在 Java 的 Web 項目中應該是無人不知的吧,你不會搭建一個 Spring 框架?作為身為一個剛剛學習Java的我都會,如果你不會的話,那可真令人憂傷。下面這篇文章主要給大家介紹了關于Spring MVC從零開始的相關資料,需要的朋友可以參考下
    2018-05-05
  • Java查詢時間段(startTime--endTime)間的數(shù)據(jù)方式

    Java查詢時間段(startTime--endTime)間的數(shù)據(jù)方式

    這篇文章主要介紹了Java查詢時間段(startTime--endTime)間的數(shù)據(jù)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 簡單了解Java創(chuàng)建線程兩種方法

    簡單了解Java創(chuàng)建線程兩種方法

    這篇文章主要介紹了簡單了解Java創(chuàng)建線程兩種方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-02-02
  • java?jpa如何自定義sql語句

    java?jpa如何自定義sql語句

    這篇文章主要介紹了java?jpa如何自定義sql語句方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 一文徹底吃透Java程序邏輯控制

    一文徹底吃透Java程序邏輯控制

    Java程序的執(zhí)行并非永遠是從上到下逐行執(zhí)行,程序邏輯控制就是通過特定語法結構改變代碼的執(zhí)行順序、選擇執(zhí)行指定代碼、重復執(zhí)行某段代碼的核心語法,這篇文章主要介紹了Java程序邏輯控制的相關資料,需要的朋友可以參考下
    2026-05-05
  • 替換jar包中的yml,class等文件的實現(xiàn)方式

    替換jar包中的yml,class等文件的實現(xiàn)方式

    文章介紹了如何在不回退版本的情況下,替換jar包中的特定文件來修復線上bug,具體步驟包括:準備文件、下載jar包、查看文件路徑、解壓文件、替換文件、重新打包文件、驗證替換、重新上傳jar包并測試
    2025-12-12
  • JAVA日志框架之JUL、JDK原生日志框架詳解

    JAVA日志框架之JUL、JDK原生日志框架詳解

    Java語言的強大之處就是因為它強大而且成熟的生態(tài)體系,其中包括日志框架,下面這篇文章主要給大家介紹了關于JAVA日志框架之JUL、JDK原生日志框架的相關資料,需要的朋友可以參考下
    2024-01-01
  • Springboot的spring-boot-maven-plugin導入失敗的解決方案

    Springboot的spring-boot-maven-plugin導入失敗的解決方案

    這篇文章主要介紹了Springboot的spring-boot-maven-plugin導入失敗的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 關于junit測試需要的依賴

    關于junit測試需要的依賴

    這篇文章主要介紹了關于junit測試需要的依賴,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • Java Hibernate對象(瞬時態(tài),持久態(tài),脫管態(tài))詳解

    Java Hibernate對象(瞬時態(tài),持久態(tài),脫管態(tài))詳解

    這篇文章主要介紹了Java Hibernate對象(瞬時態(tài),持久態(tài),脫管態(tài))詳解的相關資料,這里對Java Hibernate對象進行了介紹及總結,需要的朋友可以參考下
    2016-11-11

最新評論

米泉市| 开原市| 陆河县| 屯昌县| 靖江市| 南和县| 景洪市| 静安区| 江安县| 南昌县| 广州市| 鹤岗市| 桂阳县| 平利县| 东源县| 遂平县| 城固县| 工布江达县| 班玛县| 大邑县| 拜城县| 伊吾县| 阿克| 五家渠市| 陈巴尔虎旗| 随州市| 东乡县| 塔河县| 本溪市| 姚安县| 南木林县| 甘洛县| 竹山县| 阳曲县| 庆阳市| 修文县| 柘荣县| 略阳县| 庆元县| 湄潭县| 秦安县|