" />

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

java連接池Druid連接回收DestroyConnectionThread&DestroyTask

 更新時(shí)間:2023年09月15日 11:59:32   作者:福  
這篇文章主要為大家介紹了java連接池Druid連接回收DestroyConnectionThread&DestroyTask示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

Druid連接池的連接回收線程DestroyThread

接上一篇文章,研究Druid連接池的連接回收線程DestroyThread,通過調(diào)用destroyTask.run->DruidDataSourcek.shrink完成過期連接的回收。

DruidDataSourcek.shrink

理解DruidDataSourcek的連接回收方法shrink有一個(gè)必要前提:Druid的getConnection方法總是從connectoins的尾部獲取連接,所以閑置連接最有可能出現(xiàn)在connections數(shù)組的頭部,閑置超期需要被回收的連接也應(yīng)該處于connections的頭部(數(shù)組下標(biāo)較小的對(duì)象)。

在這個(gè)基礎(chǔ)上,我們開始分析代碼。

public void shrink(boolean checkTime, boolean keepAlive) {
        try {
            lock.lockInterruptibly();
        } catch (InterruptedException e) {
            return;
        }

        boolean needFill = false;
        int evictCount = 0;
        int keepAliveCount = 0;
        int fatalErrorIncrement = fatalErrorCount - fatalErrorCountLastShrink;
        fatalErrorCountLastShrink = fatalErrorCount;

獲取鎖資源,并初始化控制變量

try {
            if (!inited) {
                return;
            }
            final int checkCount = poolingCount - minIdle;
            final long currentTimeMillis = System.currentTimeMillis();
            for (int i = 0; i < poolingCount; ++i) {
                DruidConnectionHolder connection = connections[i];
                if ((onFatalError || fatalErrorIncrement > 0) && (lastFatalErrorTimeMillis > connection.connectTimeMillis))  {
                    keepAliveConnections[keepAliveCount++] = connection;
                    continue;
                }

如果初始化尚未完成,則不能開始做清理動(dòng)作,直接返回。

計(jì)算checkCount,checkCount的意思是本次需要清理、或者需要檢查的連接數(shù)量,checkCount等于連接池?cái)?shù)量減去參數(shù)設(shè)置的需要保持的最小空閑連接數(shù)。很好理解,清理完成之后仍然需要確保最小空閑連接數(shù)。

之后循環(huán)逐個(gè)檢查連接池connections中的所有連接,從頭部(connections[0])開始。

如果清理過程中發(fā)生了錯(cuò)誤,并且錯(cuò)誤發(fā)生的時(shí)間是在當(dāng)前連接的連接獲取時(shí)間之后,則將當(dāng)前連接放入keepAliveConnections中,繼續(xù)檢查下一個(gè)連接。

然后:

if (checkTime) {
                    if (phyTimeoutMillis > 0) {
                        long phyConnectTimeMillis = currentTimeMillis - connection.connectTimeMillis;
                        if (phyConnectTimeMillis > phyTimeoutMillis) {
                            evictConnections[evictCount++] = connection;
                            continue;
                        }
                    }
                    long idleMillis = currentTimeMillis - connection.lastActiveTimeMillis;
                    if (idleMillis < minEvictableIdleTimeMillis
                            && idleMillis < keepAliveBetweenTimeMillis
                    ) {
                        break;
                    }
                    if (idleMillis >= minEvictableIdleTimeMillis) {
                        if (checkTime && i < checkCount) {
                            evictConnections[evictCount++] = connection;
                            continue;
                        } else if (idleMillis > maxEvictableIdleTimeMillis) {
                            evictConnections[evictCount++] = connection;
                            continue;
                        }
                    }
                    if (keepAlive && idleMillis >= keepAliveBetweenTimeMillis) {
                        keepAliveConnections[keepAliveCount++] = connection;
                    }
                }

這段代碼對(duì)應(yīng)的條件是checkTime,checkTime的意思是:是否檢查數(shù)據(jù)庫(kù)連接的空閑時(shí)間,是調(diào)用shrink方法時(shí)傳入的,destoryThread任務(wù)調(diào)用shrink方法時(shí)傳入的是true,所以會(huì)走到這段代碼邏輯中。

如果參數(shù)設(shè)置的phyTimeoutMillis,即物理連接的超時(shí)時(shí)間>0的話,則檢查當(dāng)前連接創(chuàng)建以來到現(xiàn)在的時(shí)長(zhǎng)如果已超時(shí)的話,當(dāng)前連接放入evictConnections中,準(zhǔn)備回收。

然后計(jì)算當(dāng)前連接的空閑時(shí)間idleMillis,如果空閑時(shí)間小于參數(shù)設(shè)置的連接最小空閑回收時(shí)間minEvictableIdleTimeMillis,并且也小于保持存活時(shí)間keepAliveBetweenTimeMillis,則結(jié)束當(dāng)前循環(huán),不再檢查連接池中剩余連接。

這里的邏輯其實(shí)也是基于connections的特性:數(shù)組第一個(gè)元素空閑時(shí)間最長(zhǎng),從左到右的空閑時(shí)間越來越短,如果從左到右檢查過程中發(fā)現(xiàn)當(dāng)前元素空閑時(shí)間沒有達(dá)到需要回收的時(shí)長(zhǎng)的話,就沒必要檢查連接池中后續(xù)的元素了。

否則如果當(dāng)前連接空閑時(shí)長(zhǎng)idleMillis大于等于minEvictableIdleTimeMillis的話,則判斷checkTime && i < checkCount的話則將當(dāng)前連接放入evictConnections中準(zhǔn)備回收。此處i < checkCount的意思就是,回收后的連接數(shù)量仍然能夠確保最小空閑連接數(shù)的要求,則直接回收當(dāng)前連接。

否則,就是i>=checkCount情況,這種情況下如果發(fā)生回收的話,必然會(huì)導(dǎo)致連接池中的剩余連接數(shù)不能滿足參數(shù)設(shè)置的最小空閑連接數(shù)的要求、必須要重新創(chuàng)建連接了。但是如果空閑時(shí)長(zhǎng)大于maxEvictableIdleTimeMillis,也必須是要回收的,所以,將當(dāng)前連接放入evictConnections準(zhǔn)備回收。

有關(guān)連接回收,多說一句,連接池參數(shù)maxEvictableIdleTimeMillis一般會(huì)根據(jù)數(shù)據(jù)庫(kù)端的參數(shù)進(jìn)行配置,連接閑置超過一定時(shí)長(zhǎng)的話,數(shù)據(jù)庫(kù)會(huì)主動(dòng)關(guān)閉連接,這種情況下即使應(yīng)用端連接池不關(guān)閉連接,該連接也不可用了。所以為了確保連接可用,一般情況下應(yīng)用端數(shù)據(jù)庫(kù)連接池的maxEvictableIdleTimeMillis應(yīng)該設(shè)置為小于數(shù)據(jù)庫(kù)端的最大空閑時(shí)長(zhǎng)。

然后判斷如果keepAlive(參數(shù)設(shè)置,默認(rèn)false)并且當(dāng)前連接的空閑時(shí)間idleMillis大于等于參數(shù)設(shè)置的保活時(shí)長(zhǎng)keepAliveBetweenTimeMillis的話,則當(dāng)前連接放入keepAliveConnections中?;?。

接下來:

} else {
                    if (i &lt; checkCount) {
                        evictConnections[evictCount++] = connection;
                    } else {
                        break;
                    }
                }
            }

就是checkTime=false的情況,意思就是不檢查空閑時(shí)長(zhǎng),那么能夠確保最小空閑連接數(shù)的前提下,其他連接都可以回收,所以要把connections中小于checkCount((i < checkCount)的連接全部放入evictConnections中回收。

連接池中的連接檢查完畢,該回收連接放在evictConnections中,該?;畹姆旁趉eepAliveConnections中。接下來的代碼開始真正處理回收和?;?。

清理連接池connections

int removeCount = evictCount + keepAliveCount;
            if (removeCount &gt; 0) {
                System.arraycopy(connections, removeCount, connections, 0, poolingCount - removeCount);
                Arrays.fill(connections, poolingCount - removeCount, poolingCount, null);
                poolingCount -= removeCount;
            }

計(jì)算需要移除的連接數(shù)量removeCount等于回收數(shù)量與?;顢?shù)量之和,然后將connections中的位于removeCount之后的元素前移,使其處于connnections數(shù)組的頭部,并重新計(jì)算poolingCount。

接下來計(jì)算?;顢?shù)量

keepAliveCheckCount += keepAliveCount;

            if (keepAlive &amp;&amp; poolingCount + activeCount &lt; minIdle) {
                needFill = true;
            }
        } finally {
            lock.unlock();
        }

累加keepAliveCheckCount,并且判斷如果連接池?cái)?shù)量小于最小空閑數(shù)的話,設(shè)置needFill為true。

釋放鎖資源。

然后回收連接:

if (evictCount &gt; 0) {
            for (int i = 0; i &lt; evictCount; ++i) {
                DruidConnectionHolder item = evictConnections[i];
                Connection connection = item.getConnection();
                JdbcUtils.close(connection);
                destroyCountUpdater.incrementAndGet(this);
            }
            Arrays.fill(evictConnections, null);
        }

將evictConnections中的連接逐個(gè)回收:關(guān)閉連接,并累加destroyCount,并重新初始化evictConnections。

接下來處理?;钸B接:

if (keepAliveCount &gt; 0) {
            // keep order
            for (int i = keepAliveCount - 1; i &gt;= 0; --i) {
                DruidConnectionHolder holer = keepAliveConnections[i];
                Connection connection = holer.getConnection();
                holer.incrementKeepAliveCheckCount();

                boolean validate = false;
                try {
                    this.validateConnection(connection);
                    validate = true;
                } catch (Throwable error) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("keepAliveErr", error);
                    }
                    // skip
                }

                boolean discard = !validate;
                if (validate) {
                    holer.lastKeepTimeMillis = System.currentTimeMillis();
                    boolean putOk = put(holer, 0L);
                    if (!putOk) {
                        discard = true;
                    }
                }

                if (discard) {
                    try {
                        connection.close();
                    } catch (Exception e) {
                        // skip
                    }

                    lock.lock();
                    try {
                        discardCount++;

                        if (activeCount + poolingCount &lt;= minIdle) {
                            emptySignal();
                        }
                    } finally {
                        lock.unlock();
                    }
                }
            }
            this.getDataSourceStat().addKeepAliveCheckCount(keepAliveCount);
            Arrays.fill(keepAliveConnections, null);
        }

逐個(gè)處理keepAliveConnections中的連接

調(diào)用validateConnection方法檢查當(dāng)前連接是否可用,如果連接仍然可用,則更新連接的lastKeepTimeMillis為當(dāng)前系統(tǒng)時(shí)間后,調(diào)用put方法將連接重新放回連接池connections中。如果放回失敗則關(guān)閉連接。連接關(guān)閉后檢查當(dāng)前連接池?cái)?shù)量activeCount + poolingCount <= minIdle則調(diào)用emptySignal();創(chuàng)建連接。

之后將keepAliveCount加入到連接統(tǒng)計(jì)分析數(shù)據(jù)中,重置keepAliveConnections數(shù)組。

最后:

if (needFill) {
            lock.lock();
            try {
                int fillCount = minIdle - (activeCount + poolingCount + createTaskCount);
                for (int i = 0; i &lt; fillCount; ++i) {
                    emptySignal();
                }
            } finally {
                lock.unlock();
            }
        } else if (onFatalError || fatalErrorIncrement &gt; 0) {
            lock.lock();
            try {
                emptySignal();
            } finally {
                lock.unlock();
            }
        }
    }

檢查如果needFill,說明當(dāng)前連接池?cái)?shù)量不能滿足參數(shù)設(shè)置的最小空閑連接數(shù),則獲取鎖資源,計(jì)算需要?jiǎng)?chuàng)建的連接數(shù),調(diào)用emptySignal();創(chuàng)建連接填充連接池直到連接數(shù)滿足要求。

否則,如果發(fā)生錯(cuò)誤onFatalError,說明有可能創(chuàng)建連接發(fā)生錯(cuò)誤,則調(diào)用emptySignal(),檢查并繼續(xù)創(chuàng)建連接。

Druid連接回收部分的代碼分析完畢!

小結(jié)

通過兩篇文章學(xué)習(xí)分析了Druid連接池的初始化及連接回收過程,還有連接獲取及關(guān)閉兩部分重要內(nèi)容,下一篇文章繼續(xù)分析,更多關(guān)于java連接池Druid連接回收的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java學(xué)習(xí)筆記_關(guān)于字符串概述

    java學(xué)習(xí)筆記_關(guān)于字符串概述

    下面小編就為大家?guī)硪黄猨ava學(xué)習(xí)筆記_關(guān)于字符串概述。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05
  • shiro+jwt登陸驗(yàn)證實(shí)現(xiàn)教程

    shiro+jwt登陸驗(yàn)證實(shí)現(xiàn)教程

    本文介紹了如何使用Shiro和JWT實(shí)現(xiàn)登錄認(rèn)證,并通過自定義Realm和過濾器來完成身份驗(yàn)證和授權(quán),通過測(cè)試,展示了如何攜帶JWT令牌進(jìn)行接口訪問,并驗(yàn)證了其有效期
    2026-03-03
  • Spring?data?jpa緩存機(jī)制使用總結(jié)

    Spring?data?jpa緩存機(jī)制使用總結(jié)

    這篇文章主要介紹了Spring?data?jpa緩存機(jī)制使用總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • Java中的forEach循環(huán)詳細(xì)解讀

    Java中的forEach循環(huán)詳細(xì)解讀

    這篇文章主要介紹了Java中的forEach循環(huán)詳細(xì)解讀,不要再foreach循環(huán)里面進(jìn)行元素的add和remove,如果你非要進(jìn)行remove元素,那么請(qǐng)使用Iterator方式,如果存在并發(fā),那么你一定要選擇加鎖,需要的朋友可以參考下
    2023-12-12
  • Java調(diào)用DeepSeek?API的8個(gè)高頻坑與解決方法

    Java調(diào)用DeepSeek?API的8個(gè)高頻坑與解決方法

    現(xiàn)在大模型開發(fā)特別火,DeepSeek?因?yàn)橹形睦斫夂?、反?yīng)快、還便宜,不少?Java?開發(fā)者都用它,本文整理了最常踩的?8?個(gè)坑,希望對(duì)大家有所幫助
    2025-12-12
  • Java構(gòu)造函數(shù)里的一些坑記錄super()和this()

    Java構(gòu)造函數(shù)里的一些坑記錄super()和this()

    這篇文章主要介紹了Java構(gòu)造函數(shù)里的一些坑記錄super()和this(),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • 從入門到精通:Java List遍歷修改的完全指南

    從入門到精通:Java List遍歷修改的完全指南

    在Java中,我們經(jīng)常需要遍歷List集合并對(duì)其中的元素進(jìn)行修改,這可以通過使用迭代器或for循環(huán)來實(shí)現(xiàn),需要的朋友可以參考下
    2023-10-10
  • @FeignClient注入找不到異常如何解決

    @FeignClient注入找不到異常如何解決

    這篇文章主要介紹了@FeignClient注入找不到異常的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Java規(guī)則引擎easy-rules詳細(xì)介紹

    Java規(guī)則引擎easy-rules詳細(xì)介紹

    本文主要介紹了Java規(guī)則引擎easy-rules詳細(xì)介紹,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • SpringBoot熱重啟配置詳解

    SpringBoot熱重啟配置詳解

    在本篇文章里小編給大家分享的是關(guān)于SpringBoot熱重啟配置知識(shí)點(diǎn)內(nèi)容,需要的朋友們可以學(xué)習(xí)下。
    2020-02-02

最新評(píng)論

资阳市| 兰州市| 神木县| 阜平县| 甘德县| 普宁市| 耒阳市| 岑巩县| 新乐市| 柳江县| 榕江县| 涡阳县| 锦州市| 楚雄市| 张掖市| 怀化市| 三河市| 蒙城县| 新和县| 平乐县| 永泰县| 通化县| 龙里县| 东丰县| 佛冈县| 台东市| 青海省| 靖远县| 齐齐哈尔市| 顺平县| 临湘市| 肇庆市| 慈溪市| 洪湖市| 靖州| 黑河市| 孟连| 宁海县| 新宁县| 梁河县| 准格尔旗|