国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

支持生產(chǎn)阻塞的線程池

yunhao / 1879人閱讀

摘要:參考類(lèi)似的思路,最簡(jiǎn)單的做法,我們可以直接定義一個(gè),當(dāng)隊(duì)列滿時(shí)改為調(diào)用來(lái)實(shí)現(xiàn)生產(chǎn)者的阻塞這樣,我們就無(wú)需再關(guān)心和的邏輯,只要把精力集中在生產(chǎn)者和消費(fèi)者線程的實(shí)現(xiàn)邏輯上,只管往線程池提交任務(wù)就行了。

在各種并發(fā)編程模型中,生產(chǎn)者-消費(fèi)者模式大概是最常用的了。在實(shí)際工作中,對(duì)于生產(chǎn)消費(fèi)的速度,通常需要做一下權(quán)衡。通常來(lái)說(shuō),生產(chǎn)任務(wù)的速度要大于消費(fèi)的速度。一個(gè)細(xì)節(jié)問(wèn)題是,隊(duì)列長(zhǎng)度,以及如何匹配生產(chǎn)和消費(fèi)的速度。

一個(gè)典型的生產(chǎn)者-消費(fèi)者模型如下:

在并發(fā)環(huán)境下利用J.U.C提供的Queue實(shí)現(xiàn)可以很方便地保證生產(chǎn)和消費(fèi)過(guò)程中的線程安全。這里需要注意的是,Queue必須設(shè)置初始容量,防止生產(chǎn)者生產(chǎn)過(guò)快導(dǎo)致隊(duì)列長(zhǎng)度暴漲,最終觸發(fā)OutOfMemory。

對(duì)于一般的生產(chǎn)快于消費(fèi)的情況。當(dāng)隊(duì)列已滿時(shí),我們并不希望有任何任務(wù)被忽略或得不到執(zhí)行,此時(shí)生產(chǎn)者可以等待片刻再提交任務(wù),更好的做法是,把生產(chǎn)者阻塞在提交任務(wù)的方法上,待隊(duì)列未滿時(shí)繼續(xù)提交任務(wù),這樣就沒(méi)有浪費(fèi)的空轉(zhuǎn)時(shí)間了。阻塞這一點(diǎn)也很容易,BlockingQueue就是為此打造的,ArrayBlockingQueue和LinkedBlockingQueue在構(gòu)造時(shí)都可以提供容量做限制,其中LinkedBlockingQueue是在實(shí)際操作隊(duì)列時(shí)在每次拿到鎖以后判斷容量。

更進(jìn)一步,當(dāng)隊(duì)列為空時(shí),消費(fèi)者拿不到任務(wù),可以等一會(huì)兒再拿,更好的做法是,用BlockingQueue的take方法,阻塞等待,當(dāng)有任務(wù)時(shí)便可以立即獲得執(zhí)行,建議調(diào)用take的帶超時(shí)參數(shù)的重載方法,超時(shí)后線程退出。這樣當(dāng)生產(chǎn)者事實(shí)上已經(jīng)停止生產(chǎn)時(shí),不至于讓消費(fèi)者無(wú)限等待。

于是一個(gè)高效的支持阻塞的生產(chǎn)消費(fèi)模型就實(shí)現(xiàn)了。

等一下,既然J.U.C已經(jīng)幫我們實(shí)現(xiàn)了線程池,為什么還要采用這一套東西?直接用ExecutorService不是更方便?

我們來(lái)看一下ThreadPoolExecutor的基本結(jié)構(gòu):

可以看到,在ThreadPoolExecutor中,BlockingQueue和Consumer部分已經(jīng)幫我們實(shí)現(xiàn)好了,并且直接采用線程池的實(shí)現(xiàn)還有很多優(yōu)勢(shì),例如線程數(shù)的動(dòng)態(tài)調(diào)整等。

但問(wèn)題在于,即便你在構(gòu)造ThreadPoolExecutor時(shí)手動(dòng)指定了一個(gè)BlockingQueue作為隊(duì)列實(shí)現(xiàn),事實(shí)上當(dāng)隊(duì)列滿時(shí),execute方法并不會(huì)阻塞,原因在于ThreadPoolExecutor調(diào)用的是BlockingQueue非阻塞的offer方法:

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
        if (runState == RUNNING && workQueue.offer(command)) {
            if (runState != RUNNING || poolSize == 0)
                ensureQueuedTaskHandled(command);
        }
        else if (!addIfUnderMaximumPoolSize(command))
            reject(command); // is shutdown or saturated
    }
}

這時(shí)候就需要做一些事情來(lái)達(dá)成一個(gè)結(jié)果:當(dāng)生產(chǎn)者提交任務(wù),而隊(duì)列已滿時(shí),能夠讓生產(chǎn)者阻塞住,等待任務(wù)被消費(fèi)。

關(guān)鍵在于,在并發(fā)環(huán)境下,隊(duì)列滿不能由生產(chǎn)者去判斷,不能調(diào)用ThreadPoolExecutor.getQueue().size()來(lái)判斷隊(duì)列是否滿。

線程池的實(shí)現(xiàn)中,當(dāng)隊(duì)列滿時(shí)會(huì)調(diào)用構(gòu)造時(shí)傳入的RejectedExecutionHandler去拒絕任務(wù)的處理。默認(rèn)的實(shí)現(xiàn)是AbortPolicy,直接拋出一個(gè)RejectedExecutionException。

幾種拒絕策略在這里就不贅述了,這里和我們的需求比較接近的是CallerRunsPolicy,這種策略會(huì)在隊(duì)列滿時(shí),讓提交任務(wù)的線程去執(zhí)行任務(wù),相當(dāng)于讓生產(chǎn)者臨時(shí)去干了消費(fèi)者干的活兒,這樣生產(chǎn)者雖然沒(méi)有被阻塞,但提交任務(wù)也會(huì)被暫停。

public static class CallerRunsPolicy implements RejectedExecutionHandler {
    /**
     * Creates a CallerRunsPolicy.
     */
    public CallerRunsPolicy() { }

    /**
     * Executes task r in the caller"s thread, unless the executor
     * has been shut down, in which case the task is discarded.
     * @param r the runnable task requested to be executed
     * @param e the executor attempting to execute this task
     */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run();
        }
    }
}

但這種策略也有隱患,當(dāng)生產(chǎn)者較少時(shí),生產(chǎn)者消費(fèi)任務(wù)的時(shí)間里,消費(fèi)者可能已經(jīng)把任務(wù)都消費(fèi)完了,隊(duì)列處于空狀態(tài),當(dāng)生產(chǎn)者執(zhí)行完任務(wù)后才能再繼續(xù)生產(chǎn)任務(wù),這個(gè)過(guò)程中可能導(dǎo)致消費(fèi)者線程的饑餓。

參考類(lèi)似的思路,最簡(jiǎn)單的做法,我們可以直接定義一個(gè)RejectedExecutionHandler,當(dāng)隊(duì)列滿時(shí)改為調(diào)用BlockingQueue.put來(lái)實(shí)現(xiàn)生產(chǎn)者的阻塞:

new RejectedExecutionHandler() {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        if (!executor.isShutdown()) {
            try {
                executor.getQueue().put(r);
            } catch (InterruptedException e) {
                // should not be interrupted
            }
        }
    }
};

這樣,我們就無(wú)需再關(guān)心Queue和Consumer的邏輯,只要把精力集中在生產(chǎn)者和消費(fèi)者線程的實(shí)現(xiàn)邏輯上,只管往線程池提交任務(wù)就行了。

相比最初的設(shè)計(jì),這種方式的代碼量能減少不少,而且能避免并發(fā)環(huán)境的很多問(wèn)題。當(dāng)然,你也可以采用另外的手段,例如在提交時(shí)采用信號(hào)量做入口限制等,但是如果僅僅是要讓生產(chǎn)者阻塞,那就顯得復(fù)雜了。


via ifeve

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/64042.html

相關(guān)文章

  • 想進(jìn)大廠?50個(gè)多線程面試題,你會(huì)多少?(一)

    摘要:下面是線程相關(guān)的熱門(mén)面試題,你可以用它來(lái)好好準(zhǔn)備面試。線程安全問(wèn)題都是由全局變量及靜態(tài)變量引起的。持有自旋鎖的線程在之前應(yīng)該釋放自旋鎖以便其它線程可以獲得自旋鎖。 最近看到網(wǎng)上流傳著,各種面試經(jīng)驗(yàn)及面試題,往往都是一大堆技術(shù)題目貼上去,而沒(méi)有答案。 不管你是新程序員還是老手,你一定在面試中遇到過(guò)有關(guān)線程的問(wèn)題。Java語(yǔ)言一個(gè)重要的特點(diǎn)就是內(nèi)置了對(duì)并發(fā)的支持,讓Java大受企業(yè)和程序員...

    wow_worktile 評(píng)論0 收藏0
  • 使用 Executors,ThreadPoolExecutor,創(chuàng)建線程,源碼分析理解

    摘要:源碼分析創(chuàng)建可緩沖的線程池。源碼分析使用創(chuàng)建線程池源碼分析的構(gòu)造函數(shù)構(gòu)造函數(shù)參數(shù)核心線程數(shù)大小,當(dāng)線程數(shù),會(huì)創(chuàng)建線程執(zhí)行最大線程數(shù),當(dāng)線程數(shù)的時(shí)候,會(huì)把放入中保持存活時(shí)間,當(dāng)線程數(shù)大于的空閑線程能保持的最大時(shí)間。 之前創(chuàng)建線程的時(shí)候都是用的 newCachedThreadPoo,newFixedThreadPool,newScheduledThreadPool,newSingleThr...

    Chiclaim 評(píng)論0 收藏0
  • Java SDK 并發(fā)包全面總結(jié)

    摘要:一和并發(fā)包中的和主要解決的是線程的互斥和同步問(wèn)題,這兩者的配合使用,相當(dāng)于的使用。寫(xiě)鎖與讀鎖之間互斥,一個(gè)線程在寫(xiě)時(shí),不允許讀操作。的注意事項(xiàng)不支持重入,即不可反復(fù)獲取同一把鎖。沒(méi)有返回值,也就是說(shuō)無(wú)法獲取執(zhí)行結(jié)果。 一、Lock 和 Condition Java 并發(fā)包中的 Lock 和 Condition 主要解決的是線程的互斥和同步問(wèn)題,這兩者的配合使用,相當(dāng)于 synchron...

    luckyyulin 評(píng)論0 收藏0
  • 線程中你不容錯(cuò)過(guò)一些細(xì)節(jié)

    摘要:第二還是大家對(duì)線程池的理解不夠深刻,比如今天要探討的內(nèi)容。我認(rèn)為線程池它就是一個(gè)調(diào)度任務(wù)的工具。而在線程池這個(gè)場(chǎng)景中卻恰好就是要利用它只是一個(gè)普通方法調(diào)用。 showImg(https://segmentfault.com/img/remote/1460000018653817); 背景 上周分享了一篇《一個(gè)線程罷工的詭異事件》,最近也在公司內(nèi)部分享了這個(gè)案例。 無(wú)獨(dú)有偶,在內(nèi)部分享的...

    kgbook 評(píng)論0 收藏0
  • Java并發(fā)編程筆記(二)

    摘要:本文探討并發(fā)中的其它問(wèn)題線程安全可見(jiàn)性活躍性等等。當(dāng)閉鎖到達(dá)結(jié)束狀態(tài)時(shí),門(mén)打開(kāi)并允許所有線程通過(guò)。在從返回時(shí)被叫醒時(shí),線程被放入鎖池,與其他線程競(jìng)爭(zhēng)重新獲得鎖。 本文探討Java并發(fā)中的其它問(wèn)題:線程安全、可見(jiàn)性、活躍性等等。 在行文之前,我想先推薦以下兩份資料,質(zhì)量很高:極客學(xué)院-Java并發(fā)編程讀書(shū)筆記-《Java并發(fā)編程實(shí)戰(zhàn)》 線程安全 《Java并發(fā)編程實(shí)戰(zhàn)》中提到了太多的術(shù)語(yǔ)...

    NickZhou 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<