Java線程池基礎(chǔ)知識

一则拷、線程池簡介

Java并發(fā)編程中夹界,我們常常使用以下兩種方法來開啟一個新的線程來并發(fā)完成某些任務(wù):

  1. 寫一個Thread類的子類冀惭,并重寫run方法染坯,再在主線程中調(diào)用子類的start方法碌上,開啟線程:
public class MyThread extends Thread {
    ...
    @Override
    public void run() {
        doMyTask();
    }
    ...
    public static void main(String[] args) {
        Thread t1 = new MyTread("ThreadName");
        t1.start(); //啟動線程
    }
}
  1. 寫一個類實現(xiàn)Runnable接口倚评,并實現(xiàn)run方法浦徊,主線程中創(chuàng)建一個Thread實例,在構(gòu)造方法中傳入Runnable實現(xiàn)類的實例天梧,最后調(diào)用start方法盔性,啟動線程:
public class MyRunnable implements Runnable {
        ...
        public void run() {
                doMyTask();
        }
        ...
        public static void main(String[] args) {
                MyRunnable run1 = new MyRunable();
                Thread t2 = new Thread(run1); // 構(gòu)造方法中傳入自定義的Runnable
                t2.start();
        }
}

由于Java的單繼承機(jī)制,方法1導(dǎo)致MyThread類的父類只能是Thread呢岗,不方便擴(kuò)展冕香,同時任務(wù)只能執(zhí)行一次。方法2通過實現(xiàn)接口來構(gòu)造一個可重復(fù)被線程執(zhí)行的Runnable實現(xiàn)類,實現(xiàn)方法更加靈活后豫。

但這兩種方法都不可避免地在主線程中顯式地創(chuàng)建了線程對象暂筝,并在run方法執(zhí)行完成后,會自動銷毀線程(注意:是線程硬贯,不是線程對象)焕襟。然而線程的創(chuàng)建和銷毀是非常消耗資源的,在實際的開發(fā)中饭豹,如果僅僅為了執(zhí)行一次任務(wù)就創(chuàng)建一個線程鸵赖,用完即銷毀,無疑是一種浪費拄衰。為此它褪,線程池的技術(shù)應(yīng)運而生。

類似數(shù)據(jù)庫的連接池翘悉,程序每次需要接入數(shù)據(jù)庫時茫打,就從連接池中獲取一個空閑連接,使用完成后再歸還給連接池妖混。線程池管理并提供程序并發(fā)執(zhí)行任務(wù)所需的線程資源老赤。本文主要介紹有關(guān)Java線程池的基本原理以及常用的線程池對象實例。

二制市、線程池的核心類——ThreadPoolExecutor類

通過如下方法抬旺,可以方便地構(gòu)建一個固定大小的線程池實例。調(diào)用execute方法祥楣,傳入(提交)Runnable對象執(zhí)行任務(wù)开财。

public class MyRunnable implements Runnable {
        ...
        public void run() {
                doMyTask();
        }
...
        public static void main(String[] args) {
                MyRunnable run1 = new MyRunable();
                int threadPoolSize = 5; // 固定大小線程池的容量
                ExecutorService exec = Executors.newFixedThreadPool(threadPoolSize);
                exec.execute(run1); // 執(zhí)行任務(wù)
        }
}

進(jìn)入Executors類中,可以看到返回線程池實例的靜態(tài)方法newFixedThreadPool調(diào)用的是java.util.concurrent.ThreadPoolExecutor類的構(gòu)造方法误褪,并返回一個ExecutorService實例责鳍。ThreadPoolExecutor類是java線程池最核心的類,通過傳入其構(gòu)造方法的參數(shù)不同兽间,JDK實現(xiàn)了多種類型線程池历葛。雖然ThreadPoolExecutor提供了四個構(gòu)造方法,但調(diào)用的都是參數(shù)最多的那一個:

public ThreadPoolExecutor(int corePoolSize, // 核心線程數(shù)量
                                             int maximumPoolSize, // 最大線程數(shù)量(核心+工作)
                                             long keepAliveTime, // 空閑工作線程的存活時間
                                             TimeUnit unit, // 時間單位
                                             BlockingQueue<Runnable> workQueue, // 工作隊列
                                             ThreadFactory threadFactory, // 線程的創(chuàng)建工廠
                                             RejectedExecutionHandler handler // 拒絕策略)

2.1 核心線程與工作線程

線程池中的線程分為核心線程和工作線程渡八。corePoolSize為允許的最大核心線程數(shù)量啃洋,maximumPoolSize-corePoolSize為工作線程最大數(shù)量传货。核心線程與工作線程的區(qū)別就像一個公司的正式員工和臨時工,公司會一直“養(yǎng)”著正式員工工作宏娄,當(dāng)工作太多正式員工不夠時问裕,才會聘請臨時工。

大部分線程池都會維護(hù)一定數(shù)量的核心線程(Cached線程池里都是工作線程孵坚,后面會介紹)粮宛,當(dāng)所有核心線程都忙碌,且工作隊列已滿卖宠,若再有任務(wù)提交的話巍杈,就會在maximumPoolSize允許的范圍內(nèi),創(chuàng)建工作線程來執(zhí)行任務(wù)(任務(wù)提交的具體執(zhí)行步驟后面會提)扛伍。若有工作線程空閑了筷畦,沒事做,并超過了keepAliveTime(單位TimeUnit)時間刺洒,則會自動銷毀鳖宾,就像公司會把多余的臨時工開除一樣。

通過設(shè)置類中的allowCoreThreadTimeOut變量為true逆航,也可以令核心線程空閑超過keepAliveTime的時間后也自動銷毀鼎文。

2.2工作隊列workQueue

當(dāng)調(diào)用線程池的execute方法傳入一個Runnable對象時(本文中稱“提交一個任務(wù)”),并不會立即執(zhí)行該任務(wù)因俐,而是將任務(wù)放置進(jìn)一個阻塞隊列(BlockingQueue)中,線程池中的線程會從隊列中取出任務(wù)拇惋,執(zhí)行,再取新任務(wù)抹剩。這是一種生產(chǎn)者-消費者模式撑帖,目的是將任務(wù)的提交和執(zhí)行解耦開來。基本的工作隊列有三種:無界隊列吧兔,有界隊列和同步移交(Synchronous Handoff)磷仰。工作隊列一般要與線程池的大小搭配選擇。

LinkedBlockingQueue類在Executors中用作newFixedThreadPool和newSingleThreadPool的工作隊列境蔼。這兩個工廠方法調(diào)用LinkedBlockingQueue()來創(chuàng)建一個阻塞隊列實例。從LinkedBlockingQueue類的構(gòu)造方法中可以看出伺通,隊列容量為Integer.MAX_VALUE,近乎“無窮大”箍土,此時LinkedBlockingQueue類是用作無界隊列。

public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
}

public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity; // 設(shè)置隊列容量
        last = head = new Node<E>(null);
}

ArrayBlockingQueue罐监、有界LinkedBlockingQueue以及PriorityBlockingQueue是常用的有界隊列吴藻,能夠避免任務(wù)的無限制提交導(dǎo)致的OOM(OutOfMemory異常)。ArrayBlockingQueue和LinkedBlockingQueue是FIFO隊列(公平)弓柱,消費者線程按照任務(wù)的阻塞順序來從隊列中取出任務(wù)執(zhí)行沟堡。PriorityBlockingQueue中的元素具備優(yōu)先級順序侧但,默認(rèn)根據(jù)自然順序排列,或者在構(gòu)造方法中傳入的Comparator來定義航罗。

SynchronousQueue是一種同步移交隊列禀横。這種隊列非常特殊,內(nèi)部并不維護(hù)一個實際的隊列粥血,而是將生產(chǎn)者線程提供的任務(wù)直接轉(zhuǎn)交給消費者線程進(jìn)行執(zhí)行柏锄。這個隊列用于構(gòu)造newCachedThreadPool,這是一個“無限大”的線程池复亏,提交的任務(wù)會“放”進(jìn)SychronousQueue隊列中趾娃,如果有空閑線程,則直接轉(zhuǎn)交并執(zhí)行缔御,否則新建一個線程執(zhí)行剛提交的任務(wù)抬闷。當(dāng)然也可以在定制的ThreadPoolExecutor中使用同步移交,但是若線程池已滿耕突,則任務(wù)會被拒絕策略處理笤成。

舉個例子描述下三種阻塞隊列的不同。有AB兩組服務(wù)生負(fù)責(zé)洗盤子有勾,A組負(fù)責(zé)把盤子沖水洗干凈疹启,B組負(fù)責(zé)把盤子擦干水。A組把盤子洗干凈后一個個放在盤子架上蔼卡,B組從盤子架上取出盤子擦干凈喊崖。這個盤子架的容量是否有限,就說明是無界隊列或者有界隊列雇逞。同步移交不同之處在于荤懂,A組洗完盤子后,就看看有沒有B組空閑的服務(wù)員塘砸,有的話就把盤子直接交給他讓他擦干凈节仿,沒有的話就拿在手上等待是否有空閑的B組服務(wù)員,一段時間后還是沒有空閑服務(wù)員掉蔬,于是就把盤子丟進(jìn)垃圾桶里廊宪。整個過程沒有將盤子擺放在盤子架上或者取出的操作。

2.3 拒絕策略

拒絕策略女轿,又稱飽和策略箭启。故名思意,是指線程池達(dá)到飽和狀態(tài)后蛉迹,如果再有新任務(wù)提交傅寡,此時的處理策略。線程池達(dá)到飽和狀態(tài),是指線程數(shù)量最大荐操,并全部處于忙碌狀態(tài)芜抒,且工作隊列被添滿的狀態(tài)。所有的拒絕策略類都實現(xiàn)了RejectedExecutionHandler接口托启,這個接口只有一個方法rejectedExecution宅倒,用來執(zhí)行策略。常用的拒絕策略有如下幾種:

AbortPolicy是默認(rèn)的拒絕策略驾中。該策略會拋出一個非檢查異常唉堪,以供調(diào)用者捕獲,并進(jìn)一步處理肩民。

// ThreadPoolExecutor的源碼
/**
 * The default rejected execution handler
 */
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
...
// AbortPolicy類的定義
public static class AbortPolicy implements RejectedExecutionHandler {
    
    public AbortPolicy() { }
    // 直接拋出一個unchecked異常
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException("Task " + r.toString() +
                                             " rejected from " +
                                             e.toString());
    }
}

DiscardPolicy策略會“一聲不吭”地拋棄提交的新任務(wù)唠亚,從源碼中可以看出,實現(xiàn)的rejectedExecution方法為空持痰,不做任何處理灶搜。

/**
 * A handler for rejected tasks that silently discards the
 * rejected task.
 */
public static class DiscardPolicy implements RejectedExecutionHandler {
    /**
     * Creates a {@code DiscardPolicy}.
     */
    public DiscardPolicy() { }

    /**
     * Does nothing, which has the effect of discarding task r.
     *
     */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
}

DiscardOldestPolicy策略將隊列中“最老”的任務(wù)拋棄,并重新提交新的任務(wù)工窍。從源碼中可以看出割卖,即poll工作隊列隊列頭元素,然后再次提交新任務(wù)患雏。如果在工作隊列具備優(yōu)先級鹏溯,則拋棄優(yōu)先級最高的任務(wù)。

    /**
     * A handler for rejected tasks that discards the oldest unhandled
     * request and then retries {@code execute}, unless the executor
     * is shut down, in which case the task is discarded.
     */
    public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardOldestPolicy} for the given executor.
         */
        public DiscardOldestPolicy() { }

        /**
         * Obtains and ignores the next task that the executor
         * would otherwise execute, if one is immediately available,
         * and then retries execution of task r, unless the executor
         * is shut down, in which case task r is instead 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()) {
                e.getQueue().poll(); // 拋棄隊列頭元素
                e.execute(r); // 重新提交新的任務(wù)
            }
        }
    }

CallerRunsPolicy策略的思想是:誰提交的誰運行淹仑。當(dāng)A線程提交一個新任務(wù)時丙挽,發(fā)現(xiàn)線程池已經(jīng)飽和,則轉(zhuǎn)向執(zhí)行拒絕策略的rejectedExecution方法匀借。CallerRunsPolicy的rejectedExecution方法首先判斷了線程池是否關(guān)閉颜阐,然后直接調(diào)用run方法執(zhí)行任務(wù)。此時執(zhí)行任務(wù)的是A線程吓肋,在處理完成之前凳怨,A線程不能再提交新的任務(wù)。這樣的設(shè)計能夠降低生產(chǎn)者線程任務(wù)提交的速度是鬼,給消費者線程更多的時間處理任務(wù)肤舞。

圖2.1 rejectedExecution調(diào)用層級
public static class CallerRunsPolicy implements RejectedExecutionHandler {
    public CallerRunsPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) { // 判斷線程池是否關(guān)閉
            r.run(); // 直接執(zhí)行任務(wù)
        }
    }
}

三、執(zhí)行任務(wù)的核心——execute方法

3.1 ThreadPoolExecutor的超類結(jié)構(gòu)

ThreadPoolExecutor類使用生產(chǎn)者-消費者的設(shè)計模式均蜜,將任務(wù)的提交和執(zhí)行解耦開萨赁。ThreadPoolExecutor類的超類結(jié)構(gòu)如下所示。

圖3.1 ThreadPoolExecutor超類結(jié)構(gòu)層級

該類繼承自AbstractExecutorService抽象類(以下簡稱AES)兆龙,AES類由實現(xiàn)了ExecutorService接口,該接口又繼承自Executor。其中Executor接口中只有一個execute(Runnable command)方法紫皇。

ExecutorService接口繼承自Executor接口慰安,擴(kuò)展了提交Callable任務(wù)的submit方法、批量提交任務(wù)的invoke*方法以及5個管理線程池生命周期的方法聪铺。

圖3.2 ExecutorService接口的內(nèi)容

submit方法與execute方法是java線程池中兩個常用的單任務(wù)提交方法化焕,其中submit方法在AbstractExecutorService類中有相關(guān)的實現(xiàn),從源碼中可以看出铃剔,submit方法對提交的對象進(jìn)行了一定的封裝之后撒桨,最終還是調(diào)用execute方法執(zhí)行任務(wù)。

圖 3.3 AbstractExecutorService中submit方法的實現(xiàn)

而有關(guān)execute方法的實現(xiàn)键兜,都是在AbstractExecutorService類的子類或者ThreadPoolExecutor類的子類中進(jìn)行實現(xiàn)的凤类。由此可見,這個方法是整個線程池執(zhí)行任務(wù)的核心方法普气。

圖3.4 execute方法的實現(xiàn)

3.2 execute方法原理

先大致描述下Java線程池的一個任務(wù)的提交流程

圖3.5 Java線程池提交任務(wù)

本文在拒絕策略那一節(jié)描述了一個任務(wù)會在什么情況下走到被拒絕策略執(zhí)行的這一步谜疤。由上圖可知,主要要滿足3個條件:1现诀、核心線程池已滿夷磕。2、工作隊列已滿仔沿。3坐桩、線程數(shù)量達(dá)到最大(不能再添加線程了)。提交任務(wù)后封锉,程序按照1绵跷、2、3的順序來判斷條件烘浦,只要其中一個條件不滿足抖坪,則該任務(wù)則有望被成功執(zhí)行。之前學(xué)習(xí)時對為什么先判斷工作隊列是否已滿闷叉,再判斷線程數(shù)量是否最大這樣的順序有過困惑擦俐,后來想想應(yīng)該是為了優(yōu)先讓核心線程完成工作,減少創(chuàng)建和銷毀工作線程的消耗握侧。

ThreadPoolExecutor類對executor方法的實現(xiàn)如下:

public void execute(Runnable command) {
    if (command == null) // 非空判斷
        throw new NullPointerException();
    int c = ctl.get(); // 線程池狀態(tài)蚯瞧,一個AutomicInteger變量
    // 1、判斷線程數(shù)量是否小于核心線程數(shù)
    if (workerCountOf(c) < corePoolSize) { 
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 2品擎、判斷線程池是否是運行狀態(tài)埋合,并嘗試將任務(wù)添加進(jìn)工作隊列
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        // 重新檢查線程池狀態(tài),如果非運行狀態(tài)萄传,則移除之前的任務(wù)
        if (! isRunning(recheck) && remove(command))
            // 執(zhí)行拒絕策略
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 3甚颂、判斷是否可以添加工作線程來執(zhí)行任務(wù)
    else if (!addWorker(command, false))
        reject(command);
}

代碼可以分成三個部分,即對上述三個條件進(jìn)行判斷(從該方法的注釋也可以看出,參考ThreadPoolExecutor類源碼1335~1354行)振诬。

首先獲取線程池狀態(tài)蹭睡,判斷當(dāng)前線程數(shù)量是否小于核心線程數(shù)量,若滿足則使用addWorker方法添加核心線程赶么。addWorker方法的第二個參數(shù)為true表示創(chuàng)建一個核心線程肩豁。若addWorker方法執(zhí)行成功,則退出execute方法辫呻。

若以上核心線程數(shù)量已滿清钥,或者addWorker方法失敗,則再次獲取線程池狀態(tài)放闺,并在第二部分代碼檢查線程池是否為運行狀態(tài)祟昭,并嘗試將任務(wù)添加進(jìn)工作隊列。工作隊列提供offer和add方法來添加任務(wù)雄人。這兩個方法是BlockedQueue接口的方法从橘,區(qū)別在于前者返回true或false來表示執(zhí)行成功與否,后者成功返回true础钠,失敗則拋異常恰力。若成功添加任務(wù)進(jìn)工作隊列,還需根據(jù)二次判斷線程池狀態(tài)(參考1344~1349行注釋可知旗吁,這是為了并發(fā)考慮的)的結(jié)果踩萎,來決定是回滾這個這個任務(wù)添加過程,即移除(remove方法)剛剛添加的任務(wù)很钓,并交給拒絕策略處理(reject方法)香府,還是addWorker(null, false)(這里不太明白,待以后完善)码倦。

如果工作隊列已滿企孩,則嘗試使用addWorker方法添加工作線程(第二個參數(shù)傳false)。如果失敗袁稽,就只能使用reject方法將該任務(wù)交給拒絕策略處理勿璃。

execute的關(guān)鍵方法是addWorker方法。就像方法名所述推汽,新建的線程被包裝成一個Worker類并添加進(jìn)一個Set集合中來執(zhí)行任務(wù)补疑。Worker是ThreadPoolExecutor類的一個final內(nèi)部類。代碼片段如下:

private final class Worker extends AbstractQueuedSynchronizer implements Runnable
{
    final Thread thread;
    /** Initial task to run.  Possibly null. */
    Runnable firstTask;
    /** Per-thread task counter */
    volatile long completedTasks;
    /**
     * Creates with given first task and thread from ThreadFactory.
     * @param firstTask the first task (null if none)
     */
    Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }

Worker類有點意思歹撒,它繼承自AbstractQueuedSynchronizer類(AQS)(《Java并發(fā)核心類——AbstractQueuedSynchronizer類》)莲组。它的這個“爹”很有背景,是FutureTask類暖夭、ReentrantLock類锹杈、信號量Semaphore類以及閉鎖CountDownLatch類等很多并發(fā)工具類的底層實現(xiàn)撵孤,可以說java.util.concurrent包下絕大多數(shù)類都與AQS有一腿。在此就不繼續(xù)深入了嬉橙。

ThreadPoolExecutor類直接使用Worker對象來執(zhí)行任務(wù)早直。Worker類實現(xiàn)了Runnable方法,并且封裝了一個Thread對象市框,一個Thread對象對應(yīng)一個Worker,如果由于拋出Exception導(dǎo)致線程死亡糕韧,對應(yīng)的Worker對象同時也會從線程池中清除枫振。Worker的run方法調(diào)用runWorker方法,該方法會先將該Woker對象lock萤彩,然后進(jìn)入一個while循環(huán)粪滤,調(diào)用有阻塞功能的getTask方法不斷從阻塞隊列中取任務(wù),并進(jìn)行執(zhí)行雀扶,直到發(fā)生異痴刃。或者getTask返回null。實際上調(diào)用Runnable任務(wù)的run方法前后愚墓,還調(diào)用了beforeExecute和afterExecute方法予权,并進(jìn)行了比較復(fù)雜的異常處理,有興趣可以參考源碼浪册,滿精巧的扫腺。

至此,在回顧下本節(jié)開頭所述的任務(wù)提交流程村象,應(yīng)該會更加清晰笆环、易于理解。

四厚者、常見的幾種線程池

平時開發(fā)中一般通過調(diào)用java.util.concurrent.Executors類的靜態(tài)工廠方法躁劣,根據(jù)需求創(chuàng)建所需的線程池實例。常用的有以下幾種:

4.1 固定容量線程池

通過newFixedThreadPool(int nThreads)方法或newFixedThreadPool(int nThreads, ThreadFactory threadFactory)創(chuàng)建固定容量線程池库菲。傳入的int參數(shù)即為核心線程數(shù)量又是最大線程數(shù)账忘。線程工廠可以指定。

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(nThreads, nThreads, // 核心線程數(shù)量與最大線程數(shù)量一致
                                  0L, TimeUnit.MILLISECONDS, // 線程永不“過期”蝙昙,一致存活
                                  new LinkedBlockingQueue<Runnable>(), // 工作隊列為無界隊列
                                  threadFactory); // 指定線程工廠闪萄,若這個參數(shù)沒有,則使用默認(rèn)線程工廠
    }

由于最大線程數(shù)量與核心線程數(shù)一樣奇颠,因此固定線程池中所有的線程都是核心線程败去,且在停止線程池之前一直存活。當(dāng)線程數(shù)量最大烈拒,且全部忙碌時圆裕,任務(wù)被添加進(jìn)工作隊列广鳍。由于是無界隊列,則任務(wù)可以“無限制”添加吓妆。

4.2 Cached線程池

Cached線程池使用CachedThreadPool方法創(chuàng)建赊时。可選是否指定線程工廠

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE, // 核心線程數(shù)0,
                                      60L, TimeUnit.SECONDS, // 空閑線程存活時間60s
                                      new SynchronousQueue<Runnable>()); // 同步提交
    }

Cached線程池核心線程數(shù)為0行拢,線程最大數(shù)量“無限”祖秒,因此Cached線程池中都是工作線程,且空閑線程存活時間為60s舟奠。阻塞隊列為同步提交隊列竭缝,即任務(wù)提交必須有一個空閑線程來接才能成功。由于Cached線程池“無限大”沼瘫,因此任務(wù)也可以無限制提交抬纸,并且不需要排隊執(zhí)行。

4.3 單線程線程池

這個線程池通過newSingleThreadExecutor方法創(chuàng)建耿戚,核心線程數(shù)與最大線程數(shù)都指定為1湿故,并搭配無界工作隊列使用。因此提交的任務(wù)一FIFO的順序一個個執(zhí)行膜蛔。

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1, // 核心線程數(shù)與最大線程數(shù)為1
                                    0L, TimeUnit.MILLISECONDS, 
                                    new LinkedBlockingQueue<Runnable>())); // 無界的工作隊列
    }

4.4 定時線程池

顧名思義坛猪,定時線程池可以規(guī)定任務(wù)何時執(zhí)行。使用newScheduledThreadPool方法創(chuàng)建飞几,該方法返回一個ScheduledThreadPoolExecutor類實例砚哆。這個類是ThreadPoolExecutor類的子類,并實現(xiàn)了ScheduledExecutorService接口屑墨,這個接口呢又是ExecutorService接口躁锁,增加了一系列的schedule**定時調(diào)度方法。

// Executors工廠方法
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
// ScheduledThreadPoolExecutor類構(gòu)造方法之一
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

定時線程池的通過傳入?yún)?shù)指定核心線程池數(shù)量卵史,線程池最大數(shù)量“無限”战转,搭配一個DelayedWorkQueue的阻塞隊列使用,這個阻塞隊列類是ScheduledThreadPoolExecutor類的靜態(tài)內(nèi)部類以躯。

以上線程池的拒絕策略均為默認(rèn)的AbortPolicy策略槐秧,但一般情況下都走不到被拒絕策略處理這一步,畢竟他們要不是線程數(shù)量“無限大”忧设,要不就是工作隊列“無限大”提交任務(wù)刁标。

4.5 線程池線程數(shù)量的配置

線程數(shù)量的配置要根據(jù)CPU的核數(shù)以及處理任務(wù)的類型來選擇。若CPU有N個核(Runtime.getRuntime().availableProcessors()獲得可用的CPU核數(shù))址晕,IO密集型任務(wù)可以配置2*N的線程數(shù)量膀懈,CPU密集型任務(wù)可以配置N+1的線程數(shù)量。若線程池實例是通過Executors工廠方法創(chuàng)建谨垃,則可以強(qiáng)轉(zhuǎn)成ThreadPoolExecutor類型启搂,然后使用下面這些方法重新配置線程池硼控。

重新配置ThreadPoolExecutor

五、總結(jié)

使用線程池進(jìn)行并發(fā)程序開發(fā)有諸多好處:

  1. 降低資源消耗胳赌。通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗牢撼。
  2. 提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時疑苫,任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行熏版。
  3. 提高線程的可管理性。線程是稀缺資源缀匕,如果無限制的創(chuàng)建纳决,不僅會消耗系統(tǒng)資源,還會降低系統(tǒng)的穩(wěn)定性乡小,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控饵史。

本文粗略地介紹了有關(guān)Java線程池中的相關(guān)知識满钟。首先介紹線程池核心類ThreadPoolExecutor類具備的功能組件,然后簡要介紹了線程池任務(wù)提交方法execute方法的工作流程胳喷,最后介紹了常用的幾種線程池實例的配置詳細(xì)湃番。這些知識算是對自已學(xué)習(xí)并發(fā)編程的一點小總結(jié)吧。

文中多數(shù)使用“無限”這個詞吭露,并使用雙引號引起吠撮,是因為這個詞表示Integer.MAX_VALUE,為十進(jìn)制21_4748_3647讲竿。雖然這個數(shù)已經(jīng)非常大了泥兰,接近于無限,但卻并不是無限题禀,故用引號引起鞋诗。

六、參考

  1. 《Java并發(fā)編程實戰(zhàn)》
  2. 深入理解Java之線程池
  3. 聊聊并發(fā)(七)——Java中的阻塞隊列
  4. Java線程池詳解 我室友ruheng博客迈嘹,強(qiáng)烈推薦削彬!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市秀仲,隨后出現(xiàn)的幾起案子融痛,更是在濱河造成了極大的恐慌,老刑警劉巖神僵,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雁刷,死亡現(xiàn)場離奇詭異,居然都是意外死亡挑豌,警方通過查閱死者的電腦和手機(jī)安券,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門墩崩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人侯勉,你說我怎么就攤上這事鹦筹。” “怎么了址貌?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵铐拐,是天一觀的道長。 經(jīng)常有香客問我练对,道長遍蟋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任螟凭,我火速辦了婚禮虚青,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘螺男。我一直安慰自己棒厘,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布下隧。 她就那樣靜靜地躺著奢人,像睡著了一般。 火紅的嫁衣襯著肌膚如雪淆院。 梳的紋絲不亂的頭發(fā)上何乎,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機(jī)與錄音土辩,去河邊找鬼支救。 笑死,一個胖子當(dāng)著我的面吹牛脯燃,可吹牛的內(nèi)容都是我干的搂妻。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼辕棚,長吁一口氣:“原來是場噩夢啊……” “哼欲主!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起逝嚎,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤扁瓢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后补君,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體引几,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了伟桅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敞掘。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖楣铁,靈堂內(nèi)的尸體忽然破棺而出玖雁,到底是詐尸還是另有隱情,我是刑警寧澤盖腕,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布赫冬,位于F島的核電站,受9級特大地震影響溃列,放射性物質(zhì)發(fā)生泄漏劲厌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一听隐、第九天 我趴在偏房一處隱蔽的房頂上張望补鼻。 院中可真熱鬧,春花似錦雅任、人聲如沸辽幌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至虑润,卻和暖如春成玫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拳喻。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工哭当, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人冗澈。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓钦勘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親亚亲。 傳聞我的和親對象是個殘疾皇子彻采,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容