成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

從0到1玩轉(zhuǎn)線程池

darkerXi / 2799人閱讀

摘要:提交任務(wù)當(dāng)創(chuàng)建了一個(gè)線程池之后我們就可以將任務(wù)提交到線程池中執(zhí)行了。提交任務(wù)到線程池中相當(dāng)簡單,我們只要把原來傳入類構(gòu)造器的對(duì)象傳入線程池的方法或者方法就可以了。

我們一般不會(huì)選擇直接使用線程類Thread進(jìn)行多線程編程,而是使用更方便的線程池來進(jìn)行任務(wù)的調(diào)度和管理。線程池就像共享單車,我們只要在我們有需要的時(shí)候去獲取就可以了。甚至可以說線程池更棒,我們只需要把任務(wù)提交給它,它就會(huì)在合適的時(shí)候運(yùn)行了。但是如果直接使用Thread類,我們就需要在每次執(zhí)行任務(wù)時(shí)自己創(chuàng)建、運(yùn)行、等待線程了,而且很難對(duì)線程進(jìn)行整體的管理,這可不是一件輕松的事情。既然我們已經(jīng)有了線程池,那還是把這些麻煩事交給線程池來處理吧。

之前一篇介紹線程池使用及其源碼的文章篇幅太長了、跨度太大了一些,感覺不是很好理解。所以我把內(nèi)容重新組織了一下,拆為了兩篇文章,并且補(bǔ)充了一些內(nèi)容,希望能讓大家更容易地理解相關(guān)內(nèi)容。

這篇文章將從線程池的概念與一般使用入手,首先介紹線程池的一般使用。然后詳細(xì)介紹線程池中常用的可配置項(xiàng),例如任務(wù)隊(duì)列、拒絕策略等,最后會(huì)介紹四種常用的線程池配置。通過這篇文章,大家可以熟練掌握線程池的使用方式,在實(shí)踐中游刃有余地使用線程池對(duì)線程進(jìn)行靈活的調(diào)度。

閱讀本文需要對(duì)多線程編程有基本的認(rèn)識(shí),例如什么是線程、多線程解決的是什么問題等。不了解的讀者可以參考一下我之前發(fā)布的一篇文章《這一次,讓我們完全掌握J(rèn)ava多線程(2/10)》

一般我們最常用的線程池實(shí)現(xiàn)類是ThreadPoolExecutor,我們接下來會(huì)介紹這個(gè)類的基本使用方法。JDK已經(jīng)對(duì)線程池做了比較好的封裝,相信這個(gè)過程會(huì)非常輕松。

線程池的基本使用 創(chuàng)建線程池

既然線程池是一個(gè)Java類,那么最直接的使用方法一定是new一個(gè)ThreadPoolExecutor類的對(duì)象,例如ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue() )。那么這個(gè)構(gòu)造器的里每個(gè)參數(shù)是什么意思呢?我們可以暫時(shí)不用關(guān)心這些細(xì)節(jié),繼續(xù)完成線程池的使用之旅,稍后再回頭來研究這個(gè)問題。

提交任務(wù)

當(dāng)創(chuàng)建了一個(gè)線程池之后我們就可以將任務(wù)提交到線程池中執(zhí)行了。提交任務(wù)到線程池中相當(dāng)簡單,我們只要把原來傳入Thread類構(gòu)造器的Runnable對(duì)象傳入線程池的execute方法或者submit方法就可以了。execute方法和submit方法基本沒有區(qū)別,兩者的區(qū)別只是submit方法會(huì)返回一個(gè)Future對(duì)象,用于檢查異步任務(wù)的執(zhí)行情況和獲取執(zhí)行結(jié)果(異步任務(wù)完成后)。

我們可以先試試如何使用比較簡單的execute方法,代碼例子如下:

public class ThreadPoolTest {

    private static int count = 0;

    public static void main(String[] args) throws Exception {
        Runnable task = new Runnable() {
            public void run() {
                for (int i = 0; i < 1000000; ++i) {
                    synchronized (ThreadPoolTest.class) {
                        count += 1;
                    }
                }
            }
        };

        // 重要:創(chuàng)建線程池
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1, 0L,
        TimeUnit.MILLISECONDS, new LinkedBlockingQueue());

        // 重要:向線程池提交兩個(gè)任務(wù)
        threadPool.execute(task);
        threadPool.execute(task);

        // 等待線程池中的所有任務(wù)完成
        threadPool.shutdown();
        while (!threadPool.awaitTermination(1L, TimeUnit.MINUTES)) {
            System.out.println("Not yet. Still waiting for termination");
        }
        
        System.out.println("count = " + count);
    }
}

運(yùn)行之后得到的結(jié)果是兩百萬,我們成功實(shí)現(xiàn)了第一個(gè)使用線程池的程序。那么回到剛才的問題,創(chuàng)建線程池時(shí)傳入的那些參數(shù)有什么作用的呢?

深入解析線程池 創(chuàng)建線程池的參數(shù)

下面是ThreadPoolExecutor的構(gòu)造器定義:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

各個(gè)參數(shù)分別表示下面的含義:

corePoolSize,核心線程池大小,一般線程池會(huì)至少保持這么多的線程數(shù)量;

maximumPoolSize,最大線程池大小,也就是線程池最大的線程數(shù)量;

keepAliveTime和unit共同組成了一個(gè)超時(shí)時(shí)間,keepAliveTime是時(shí)間數(shù)量,unit是時(shí)間單位,單位加數(shù)量組成了最終的超時(shí)時(shí)間。這個(gè)超時(shí)時(shí)間表示如果線程池中包含了超過corePoolSize數(shù)量的線程,則在有線程空閑的時(shí)間超過了超時(shí)時(shí)間時(shí)該線程就會(huì)被銷毀;

workQueue是任務(wù)的阻塞隊(duì)列,在沒有線程池中沒有足夠的線程可用的情況下會(huì)將任務(wù)先放入到這個(gè)阻塞隊(duì)列中等待執(zhí)行。這里傳入的隊(duì)列類型就決定了線程池在處理這些任務(wù)時(shí)的策略,具體類型會(huì)在下文中介紹;

threadFactory,線程的工廠對(duì)象,線程池通過該對(duì)象創(chuàng)建線程。我們可以通過傳入自定義的實(shí)現(xiàn)了ThreadFactory接口的類來修改線程的創(chuàng)建邏輯,可以不傳,默認(rèn)使用Executors.defaultThreadFactory()作為默認(rèn)的線程工廠;

handler,拒絕策略,在線程池?zé)o法執(zhí)行或保存新提交的任務(wù)時(shí)進(jìn)行處理的對(duì)象,常用的有以下幾種策略類:

ThreadPoolExecutor.AbortPolicy,默認(rèn)策略,行為是直接拋出RejectedExecutionException異常

ThreadPoolExecutor.CallerRunsPolicy,用調(diào)用者所在的線程來執(zhí)行任務(wù)

ThreadPoolExecutor.DiscardOldestPolicy,丟棄阻塞隊(duì)列中最早提交的任務(wù),并重試execute方法

ThreadPoolExecutor.DiscardPolicy,靜默地直接丟棄任務(wù),不返回任何錯(cuò)誤

看到這里可能大部分讀者并不能理解每個(gè)參數(shù)具體的作用,接下來我們就通過線程池源代碼中使用了這些參數(shù)配置的代碼來深入理解每一個(gè)參數(shù)的意義。

execute方法的實(shí)現(xiàn)

我們一般會(huì)使用execute方法提交我們的任務(wù),那么線程池在這個(gè)過程中做了什么呢?在ThreadPoolExecutor類的execute()方法的源代碼中,我們主要做了四件事:

如果當(dāng)前線程池中的線程數(shù)小于核心線程數(shù)corePoolSize,則通過threadFactory創(chuàng)建一個(gè)新的線程,并把入?yún)⒅械娜蝿?wù)作為第一個(gè)任務(wù)傳入該線程;

如果當(dāng)前線程池中的線程數(shù)已經(jīng)達(dá)到了核心線程數(shù)corePoolSize,那么就會(huì)通過阻塞隊(duì)列workerQueueoffer方法來將任務(wù)添加到隊(duì)列中保存,并等待線程空閑后進(jìn)行執(zhí)行;

如果線程數(shù)已經(jīng)達(dá)到了corePoolSize且阻塞隊(duì)列中無法插入該任務(wù)(比如已滿),那么線程池就會(huì)再增加一個(gè)線程來執(zhí)行該任務(wù),除非線程數(shù)已經(jīng)達(dá)到了最大線程數(shù)maximumPoolSize

如果確實(shí)已經(jīng)達(dá)到了最大線程數(shù),那么就會(huì)通過拒絕策略對(duì)象handler拒絕這個(gè)任務(wù)。

總體上的執(zhí)行流程如下,左側(cè)的實(shí)心黑點(diǎn)代表流程開始,下方的黑色同心圓代表流程結(jié)束:

上面提到了線程池構(gòu)造器參數(shù)中除了超時(shí)時(shí)間之外的所有參數(shù)的作用,相信大家根據(jù)上面的流程已經(jīng)可以理解每個(gè)參數(shù)的意義了。但是有一個(gè)名詞我們還一直沒有深入講解,那就是阻塞隊(duì)列的含義。

線程池中的阻塞隊(duì)列

線程池中的阻塞隊(duì)列專門用于存放需要等待線程空閑的待執(zhí)行任務(wù),而阻塞隊(duì)列是這樣的一種數(shù)據(jù)結(jié)構(gòu),它是一個(gè)隊(duì)列(類似于一個(gè)List),可以存放0到N個(gè)元素。我們可以對(duì)這個(gè)隊(duì)列進(jìn)行插入和彈出元素的操作,彈出操作可以理解為是一個(gè)獲取并從隊(duì)列中刪除一個(gè)元素的操作。當(dāng)隊(duì)列中沒有元素時(shí),對(duì)這個(gè)隊(duì)列的獲取操作將會(huì)被阻塞,直到有元素被插入時(shí)才會(huì)被喚醒;當(dāng)隊(duì)列已滿時(shí),對(duì)這個(gè)隊(duì)列的插入操作將會(huì)被阻塞,直到有元素被彈出后才會(huì)被喚醒。

這樣的一種數(shù)據(jù)結(jié)構(gòu)非常適合于線程池的場(chǎng)景,當(dāng)一個(gè)工作線程沒有任務(wù)可處理時(shí)就會(huì)進(jìn)入阻塞狀態(tài),直到有新任務(wù)提交后才被喚醒。

在線程池中,不同的阻塞隊(duì)列類型會(huì)被線程池的行為產(chǎn)生不同的影響,下面是三種我們最常用的阻塞隊(duì)列類型:

直連隊(duì)列,以SynchronousQueue類為代表,隊(duì)列不會(huì)存儲(chǔ)任何任務(wù)。當(dāng)有任務(wù)提交線程試圖向隊(duì)列中添加待執(zhí)行任務(wù)時(shí)會(huì)被阻塞,直到有任務(wù)處理線程試圖從隊(duì)列中獲取待執(zhí)行任務(wù)時(shí)會(huì)與阻塞狀態(tài)中的任務(wù)提交線程發(fā)生直接聯(lián)系,由任務(wù)提交線程把任務(wù)直接交給任務(wù)執(zhí)行線程;

無界隊(duì)列,以LinkedBlockingQueue類為代表,隊(duì)列中可以存儲(chǔ)無限數(shù)量的任務(wù)。這種隊(duì)列永遠(yuǎn)不會(huì)因?yàn)殛?duì)列已滿導(dǎo)致任務(wù)放入隊(duì)列失敗,所以結(jié)合前面介紹的流程我們可以發(fā)現(xiàn),當(dāng)使用無界隊(duì)列時(shí),線程池中的線程最多只能達(dá)到核心線程數(shù)就不會(huì)再增長了,最大線程數(shù)maximumPoolSize參數(shù)不會(huì)產(chǎn)生作用;

有界隊(duì)列,以ArrayBlockingQueue類為代表,可以保存固定數(shù)量的任務(wù)。這種隊(duì)列在實(shí)踐中比較常用,因?yàn)樗炔粫?huì)因?yàn)楸4嫣嗳蝿?wù)導(dǎo)致資源消耗過多(無界隊(duì)列),又不會(huì)因?yàn)槿蝿?wù)提交線程被阻塞而影響到系統(tǒng)的性能(直連隊(duì)列)。總體上來說,有界隊(duì)列在實(shí)際效果上比較均衡。

閱讀execute方法的源碼

在IDE中,例如IDEA里,我們可以點(diǎn)擊我們樣例代碼里的ThreadPoolExecutor類跳轉(zhuǎn)到JDK中ThreadPoolExecutor類的源代碼。在源代碼中我們可以看到很多java.util.concurrent包的締造者大?!癉oug Lea”所留下的各種注釋,下面的圖片就是該類源代碼的一個(gè)截圖。

這些注釋的內(nèi)容非常有參考價(jià)值,建議有能力的讀者朋友可以自己閱讀一遍。下面,我們就一步步地抽絲剝繭,來揭開線程池類ThreadPoolExecutor源代碼的神秘面紗。不過這一步并不是必須的,可以跳過。

下面是ThreadPoolExecutorexecute方法帶有中文解釋的源代碼,有興趣的朋友可以和上面的流程對(duì)照起來參考一下:

public void execute(Runnable command) {
    // 檢查提交的任務(wù)是否為空
    if (command == null)
        throw new NullPointerException();
    
    // 獲取控制變量值
    int c = ctl.get();
    // 檢查當(dāng)前線程數(shù)是否達(dá)到了核心線程數(shù)
    if (workerCountOf(c) < corePoolSize) {
        // 未達(dá)到核心線程數(shù),則創(chuàng)建新線程
        // 并將傳入的任務(wù)作為該線程的第一個(gè)任務(wù)
        if (addWorker(command, true))
            // 添加線程成功則直接返回,否則繼續(xù)執(zhí)行
            return;

        // 因?yàn)榍懊嬲{(diào)用了耗時(shí)操作addWorker方法
        // 所以線程池狀態(tài)有可能發(fā)生了改變,重新獲取狀態(tài)值
        c = ctl.get();
    }

    // 判斷線程池當(dāng)前狀態(tài)是否是運(yùn)行中
    // 如果是則調(diào)用workQueue.offer方法將任務(wù)放入阻塞隊(duì)列
    if (isRunning(c) && workQueue.offer(command)) {
        // 因?yàn)閳?zhí)行了耗時(shí)操作“放入阻塞隊(duì)列”,所以重新獲取狀態(tài)值
        int recheck = ctl.get();
        // 如果當(dāng)前狀態(tài)不是運(yùn)行中,則將剛才放入阻塞隊(duì)列的任務(wù)拿出,如果拿出成功,則直接拒絕這個(gè)任務(wù)
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            // 如果線程池中沒有線程了,那就創(chuàng)建一個(gè)
            addWorker(null, false);
    }
    // 如果放入阻塞隊(duì)列失?。ㄈ珀?duì)列已滿),則添加一個(gè)線程
    else if (!addWorker(command, false))
        // 如果添加線程失?。ㄈ缫呀?jīng)達(dá)到了最大線程數(shù)),則拒絕任務(wù)
        reject(command);
}

在這段源代碼中,我們可以看到,線程池是通過addWorker方法來創(chuàng)建線程的,這里的這個(gè)Worker指的就是ThreadPoolExecutor類中用來對(duì)線程進(jìn)行包裝和管理的Worker類對(duì)象。如果想了解Worker類的具體執(zhí)行流程可以閱讀一下下一篇深入剖析線程池的任務(wù)執(zhí)行流程的文章。

超時(shí)時(shí)間

那么還有一個(gè)我們沒有提到的超時(shí)時(shí)間在這個(gè)過程中發(fā)揮了什么作用呢?從前面我們可以看出,線程數(shù)量被劃分為了核心線程數(shù)和最大線程數(shù)。當(dāng)線程沒有任務(wù)可執(zhí)行時(shí)會(huì)阻塞在從隊(duì)列中獲取新任務(wù)這個(gè)操作上,這時(shí)我們稱這個(gè)線程為空閑線程,一旦有新任務(wù)被提交,則該線程就會(huì)退出阻塞狀態(tài)并開始執(zhí)行這個(gè)新任務(wù)。

如果當(dāng)前線程池中的線程總數(shù)大于核心線程數(shù),那么只要有線程的空閑時(shí)間超過了超時(shí)時(shí)間,那么這個(gè)線程就會(huì)被銷毀;如果線程池中的線程總數(shù)小于等于核心線程數(shù),那么超時(shí)線程就不會(huì)被銷毀了(除了一些特殊情況外)。這也就是超時(shí)時(shí)間參數(shù)所發(fā)揮的作用了。

其他線程池操作 關(guān)閉線程池

在之前使用線程池執(zhí)行任務(wù)的代碼中為了等待線程池中的所有任務(wù)執(zhí)行完已經(jīng)使用了shutdown()方法,這是關(guān)閉線程池的一種方法。對(duì)于ThreadPoolExecutor,關(guān)閉線程池的方法主要有兩個(gè):

shutdown(),有序關(guān)閉線程池,調(diào)用后線程池會(huì)讓已經(jīng)提交的任務(wù)完成執(zhí)行,但是不會(huì)再接受新任務(wù)。

shutdownNow(),直接關(guān)閉線程池,線程池中正在運(yùn)行的任務(wù)會(huì)被中斷,正在等待執(zhí)行的任務(wù)不會(huì)再被執(zhí)行,但是這些還在阻塞隊(duì)列中等待的任務(wù)會(huì)被作為返回值返回。

監(jiān)控線程池運(yùn)行狀態(tài)

我們可以通過調(diào)用線程池對(duì)象上的一些方法來獲取線程池當(dāng)前的運(yùn)行信息,常用的方法有:

getTaskCount,線程池中已完成、執(zhí)行中、等待執(zhí)行的任務(wù)總數(shù)估計(jì)值。因?yàn)樵诮y(tǒng)計(jì)過程中任務(wù)會(huì)發(fā)生動(dòng)態(tài)變化,所以最后的結(jié)果并不是一個(gè)準(zhǔn)確值;

getCompletedTaskCount,線程池中已完成的任務(wù)總數(shù),這同樣是一個(gè)估計(jì)值;

getLargestPoolSize,線程池曾經(jīng)創(chuàng)建過的最大線程數(shù)量。通過這個(gè)數(shù)據(jù)可以知道線程池是否充滿過,也就是達(dá)到過maximumPoolSize;

getPoolSize,線程池當(dāng)前的線程數(shù)量;

getActiveCount,當(dāng)前線程池中正在執(zhí)行任務(wù)的線程數(shù)量估計(jì)值。

四種常用線程池

很多情況下我們也不會(huì)直接創(chuàng)建ThreadPoolExecutor類的對(duì)象,而是根據(jù)需要通過Executors的幾個(gè)靜態(tài)方法來創(chuàng)建特定用途的線程池。目前常用的線程池有四種:

可緩存線程池,使用Executors.newCachedThreadPool方法創(chuàng)建

定長線程池,使用Executors.newFixedThreadPool方法創(chuàng)建

延時(shí)任務(wù)線程池,使用Executors.newScheduledThreadPool方法創(chuàng)建

單線程線程池,使用Executors.newSingleThreadExecutor方法創(chuàng)建

下面通過這些靜態(tài)方法的源碼來具體了解一下不同類型線程池的特性與適用場(chǎng)景。

可緩存線程池

JDK中的源碼我們通過在IDE中進(jìn)行跳轉(zhuǎn)可以很方便地進(jìn)行查看,下面就是Executors.newCachedThreadPool方法中的源代碼。從代碼中我們可以看到,可緩存線程池其實(shí)也是通過直接創(chuàng)建ThreadPoolExecutor類的構(gòu)造器創(chuàng)建的,只是其中的參數(shù)都已經(jīng)被設(shè)置好了,我們可以不用做具體的設(shè)置。所以我們要觀察的重點(diǎn)就是在這個(gè)方法中具體產(chǎn)生了一個(gè)怎樣配置的ThreadPoolExecutor對(duì)象,以及這樣的線程池適用于怎樣的場(chǎng)景。

從下面的代碼中,我們可以看到,傳入ThreadPoolExecutor構(gòu)造器的值有:

- corePoolSize核心線程數(shù)為0,代表線程池中的線程數(shù)可以為0
- maximumPoolSize最大線程數(shù)為Integer.MAX_VALUE,代表線程池中最多可以有無限多個(gè)線程
- 超時(shí)時(shí)間設(shè)置為60秒,表示線程池中的線程在空閑60秒后會(huì)被回收
- 最后傳入的是一個(gè)`SynchronousQueue`類型的阻塞隊(duì)列,代表每一個(gè)新添加的任務(wù)都要馬上有一個(gè)工作線程進(jìn)行處理
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue());
}

所以可緩存線程池在添加任務(wù)時(shí)會(huì)優(yōu)先使用空閑的線程,如果沒有就創(chuàng)建一個(gè)新線程,線程數(shù)沒有上限,所以每一個(gè)任務(wù)都會(huì)馬上被分配到一個(gè)工作線程進(jìn)行執(zhí)行,不需要在阻塞隊(duì)列中等待;如果線程池長期閑置,那么其中的所有線程都會(huì)被銷毀,節(jié)約系統(tǒng)資源。

優(yōu)點(diǎn)

任務(wù)在添加后可以馬上執(zhí)行,不需要進(jìn)入阻塞隊(duì)列等待

在閑置時(shí)不會(huì)保留線程,可以節(jié)約系統(tǒng)資源

缺點(diǎn)

對(duì)線程數(shù)沒有限制,可能會(huì)過量消耗系統(tǒng)資源

適用場(chǎng)景

適用于大量短耗時(shí)任務(wù)和對(duì)響應(yīng)時(shí)間要求較高的場(chǎng)景

定長線程池

傳入ThreadPoolExecutor構(gòu)造器的值有:

corePoolSize核心線程數(shù)和maximumPoolSize最大線程數(shù)都為固定值nThreads,即線程池中的線程數(shù)量會(huì)保持在nThreads,所以被稱為“定長線程池”

超時(shí)時(shí)間被設(shè)置為0毫秒,因?yàn)榫€程池中只有核心線程,所以不需要考慮超時(shí)釋放

最后一個(gè)參數(shù)使用了無界隊(duì)列,所以在所有線程都在處理任務(wù)的情況下,可以無限添加任務(wù)到阻塞隊(duì)列中等待執(zhí)行

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue());
}

定長線程池中的線程數(shù)會(huì)逐步增長到nThreads個(gè),并且在之后空閑線程不會(huì)被釋放,線程數(shù)會(huì)一直保持在nThreads個(gè)。如果添加任務(wù)時(shí)所有線程都處于忙碌狀態(tài),那么就會(huì)把任務(wù)添加到阻塞隊(duì)列中等待執(zhí)行,阻塞隊(duì)列中任務(wù)的總數(shù)沒有上限。

優(yōu)點(diǎn)

線程數(shù)固定,對(duì)系統(tǒng)資源的消耗可控

缺點(diǎn)

在任務(wù)量暴增的情況下線程池不會(huì)彈性增長,會(huì)導(dǎo)致任務(wù)完成時(shí)間延遲

使用了無界隊(duì)列,在線程數(shù)設(shè)置過小的情況下可能會(huì)導(dǎo)致過多的任務(wù)積壓,引起任務(wù)完成時(shí)間過晚和資源被過度消耗的問題

適用場(chǎng)景

任務(wù)量峰值不會(huì)過高,且任務(wù)對(duì)響應(yīng)時(shí)間要求不高的場(chǎng)景

延時(shí)任務(wù)線程池

與之前的兩個(gè)方法不同,Executors.newScheduledThreadPool返回的是ScheduledExecutorService接口對(duì)象,可以提供延時(shí)執(zhí)行、定時(shí)執(zhí)行等功能。在線程池配置上有如下特點(diǎn):

maximumPoolSize最大線程數(shù)為無限,在任務(wù)量較大時(shí)可以創(chuàng)建大量新線程執(zhí)行任務(wù)

超時(shí)時(shí)間為0,線程空閑后會(huì)被立即銷毀

使用了延時(shí)工作隊(duì)列,延時(shí)工作隊(duì)列中的元素都有對(duì)應(yīng)的過期時(shí)間,只有過期的元素才會(huì)被彈出

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

延時(shí)任務(wù)線程池實(shí)現(xiàn)了ScheduledExecutorService接口,主要用于需要延時(shí)執(zhí)行和定時(shí)執(zhí)行的情況。

單線程線程池

單線程線程池中只有一個(gè)工作線程,可以保證添加的任務(wù)都以指定順序執(zhí)行(先進(jìn)先出、后進(jìn)先出、優(yōu)先級(jí))。但是如果線程池里只有一個(gè)線程,為什么我們還要用線程池而不直接用Thread呢?這種情況下主要有兩種優(yōu)點(diǎn):一是我們可以通過共享的線程池很方便地提交任務(wù)進(jìn)行異步執(zhí)行,而不用自己管理線程的生命周期;二是我們可以使用任務(wù)隊(duì)列并指定任務(wù)的執(zhí)行順序,很容易做到任務(wù)管理的功能。

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue()));
}
總結(jié)

在這篇文章中我們從線程池的概念和基本使用方法說起,通過execute方法的源碼深入剖析了任務(wù)提交的全過程和各個(gè)線程池構(gòu)造器參數(shù)在線程池實(shí)際運(yùn)行過程中所發(fā)揮的作用,還真正閱讀了線程池類ThreadPoolExecutor的execute方法的源代碼。最后,我們介紹了線程池的其他常用操作和四種常用的線程池。

到這里我們的線程池源代碼之旅就結(jié)束了,希望大家在看完這篇文章之后能對(duì)線程池的使用和運(yùn)行流程有了一個(gè)大概的印象。為什么說只是有了一個(gè)大概的印象呢?因?yàn)槲矣X得很多沒有相關(guān)基礎(chǔ)的讀者讀到這里可能還只是對(duì)線程池有了一個(gè)自己的認(rèn)識(shí),對(duì)其中的一些細(xì)節(jié)可能還沒有完全捕捉到。所以我建議大家在看完這篇文章后不妨再返回到文章的開頭多讀幾遍,相信第二遍的閱讀能給大家?guī)聿灰粯拥捏w驗(yàn),因?yàn)槲易约阂彩窃诘谌巫xThreadPoolExecutor類的源代碼時(shí)才真正打通了其中的一些重要關(guān)節(jié)的。

引子

在這篇文章中,我們還只是探究了線程池的基本使用方法,以及提交任務(wù)方法execute的源代碼。那么在任務(wù)提交以后是怎么被線程池所執(zhí)行的呢?在下一篇文章中我們就可以找到答案,在下一篇文章中,我們會(huì)深入剖析線程池的任務(wù)執(zhí)行流程。

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/73860.html

相關(guān)文章

  • 跟著阿里p7一起學(xué)java高并發(fā) - 第18天:玩轉(zhuǎn)java線程,這一篇就夠了

    摘要:高并發(fā)系列第篇文章。簡單的說,在使用了線程池之后,創(chuàng)建線程變成了從線程池中獲取一個(gè)空閑的線程,然后使用,關(guān)閉線程變成了將線程歸還到線程池。如果調(diào)用了線程池的方法,線程池會(huì)提前把核心線程都創(chuàng)造好,并啟動(dòng)線程池允許創(chuàng)建的最大線程數(shù)。 java高并發(fā)系列第18篇文章。 本文主要內(nèi)容 什么是線程池 線程池實(shí)現(xiàn)原理 線程池中常見的各種隊(duì)列 自定義線程創(chuàng)建的工廠 常見的飽和策略 自定義飽和策略 ...

    AdolphLWQ 評(píng)論0 收藏0
  • 線程運(yùn)行模型源碼全解析

    摘要:那么線程池到底是怎么利用類來實(shí)現(xiàn)持續(xù)不斷地接收提交的任務(wù)并執(zhí)行的呢接下來,我們通過的源代碼來一步一步抽絲剝繭,揭開線程池運(yùn)行模型的神秘面紗。 在上一篇文章《從0到1玩轉(zhuǎn)線程池》中,我們了解了線程池的使用方法,以及向線程池中提交任務(wù)的完整流程和ThreadPoolExecutor.execute方法的源代碼。在這篇文章中,我們將會(huì)從頭閱讀線程池ThreadPoolExecutor類的源代...

    MockingBird 評(píng)論0 收藏0
  • Java深入-框架技巧

    摘要:從使用到原理學(xué)習(xí)線程池關(guān)于線程池的使用,及原理分析分析角度新穎面向切面編程的基本用法基于注解的實(shí)現(xiàn)在軟件開發(fā)中,分散于應(yīng)用中多出的功能被稱為橫切關(guān)注點(diǎn)如事務(wù)安全緩存等。 Java 程序媛手把手教你設(shè)計(jì)模式中的撩妹神技 -- 上篇 遇一人白首,擇一城終老,是多么美好的人生境界,她和他歷經(jīng)風(fēng)雨慢慢變老,回首走過的點(diǎn)點(diǎn)滴滴,依然清楚的記得當(dāng)初愛情萌芽的模樣…… Java 進(jìn)階面試問題列表 -...

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

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

0條評(píng)論

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