[Java多線程編程之七] 讓程序飛 —— 線程池的使用及實(shí)現(xiàn)原理

一纬向、為什么要使用線程池迈倍?

??為了充分利用多核計(jì)算機(jī)的性能伤靠,程序需要被設(shè)計(jì)成多線程程序,保證不會(huì)出現(xiàn)某個(gè)CPU很忙啼染,某個(gè)CPU很閑的情況宴合,把線程的創(chuàng)建、通信和管理交給線程池管理迹鹅,可以讓開(kāi)發(fā)人員專注于程序的業(yè)務(wù)邏輯卦洽;另一方面,線程的創(chuàng)建需要消耗操作系統(tǒng)的資源斜棚,如果頻繁地創(chuàng)建和銷毀線程阀蒂,代價(jià)太大该窗,而線程池對(duì)此做了優(yōu)化,線程池中預(yù)先創(chuàng)建好了一組空閑的線程蚤霞,當(dāng)程序運(yùn)行需要時(shí)酗失,從線程池中取用空閑線程,當(dāng)程序運(yùn)行結(jié)束時(shí)争便,工作線程又重置為空閑線程放到線程池中级零,提高系統(tǒng)資源的利用效率。

線程是否越多越好滞乙?

(1)線程在java中是一個(gè)對(duì)象奏纪,更是操作系統(tǒng)的資源,線程的創(chuàng)建斩启、銷毀需要時(shí)間序调,如果創(chuàng)建時(shí)間 + 銷毀時(shí)間 > 任務(wù)執(zhí)行時(shí)間,就不劃算兔簇。
(2)java對(duì)象占用堆內(nèi)存发绢,操作系統(tǒng)線程占用系統(tǒng)內(nèi)存,根據(jù)jvm規(guī)范垄琐,一個(gè)線程默認(rèn)最大棧大小為1M边酒,這個(gè)棧空間是需要從系統(tǒng)內(nèi)存中分配的狸窘,如果線程過(guò)多墩朦,也會(huì)消耗過(guò)多內(nèi)存,當(dāng)超過(guò)負(fù)荷時(shí)程序可能會(huì)發(fā)生異撤埽或錯(cuò)誤氓涣。
(3)線程數(shù)過(guò)多,會(huì)導(dǎo)致操作系統(tǒng)需要頻繁切換上下文陋气,這需要消耗性能劳吠,每個(gè)線程的執(zhí)行效率反而會(huì)下降。

二巩趁、線程池原理

1痒玩、線程池管理器

??用于創(chuàng)建并管理線程池,包括創(chuàng)建線程池议慰、銷毀線程池凰荚,添加新的執(zhí)行任務(wù)等,一般會(huì)限定線程池大小褒脯,避免像沒(méi)有線程池管理時(shí)無(wú)限制地去創(chuàng)建線程導(dǎo)致線程數(shù)過(guò)多導(dǎo)致性能下降的情況便瑟。



2、工作線程

??線程池中的線程番川,當(dāng)應(yīng)用程序需要一個(gè)新的線程時(shí)到涂,線程池為其創(chuàng)建一個(gè)工作線程脊框;當(dāng)該線程需要執(zhí)行的程序結(jié)束時(shí),線程不會(huì)被銷毀践啄,而是回到線程池浇雹,繼續(xù)等待執(zhí)行任務(wù),這就意味著工作線程可以循環(huán)地執(zhí)行不同任務(wù)屿讽。



3昭灵、任務(wù)接口

??每個(gè)提交到線程池的任務(wù)都必須實(shí)現(xiàn)的接口,以供工作線程調(diào)度任務(wù)的執(zhí)行伐谈,它主要規(guī)定了任務(wù)的入口烂完、任務(wù)執(zhí)行完后的收尾工作、任務(wù)的執(zhí)行狀態(tài)等诵棵,可以把任務(wù)接口看成是工作線程執(zhí)行目標(biāo)的一個(gè)格式抠蚣。

4、任務(wù)隊(duì)列

??線程池所能創(chuàng)建的工作線程數(shù)是有限的履澳,當(dāng)提交的任務(wù)數(shù)很多導(dǎo)致線程池暫時(shí)沒(méi)有線程可去執(zhí)行時(shí)嘶窄,線程池會(huì)將其放入到任務(wù)隊(duì)列中,可以把任務(wù)隊(duì)列看成是一種緩沖機(jī)制距贷,等線程池里又有空閑的工作線程里柄冲,線程池就可以從任務(wù)隊(duì)列中取出任務(wù)給工作線程去執(zhí)行。



三忠蝗、線程池API —— 接口定義和實(shí)現(xiàn)類

類型 名稱 描述
接口 Executor 最上層的接口羊初,定義了執(zhí)行任務(wù)的方法execute
接口 ExecutorService 繼承了Executor接口,拓展了Callable什湘、Future、關(guān)閉方法
抽閑實(shí)現(xiàn)類 AbstractExecutorService 繼承了ExecutorService接口晦攒,實(shí)現(xiàn)了submit闽撤、invokeAnyinvokeAll方法脯颜,將提交的任務(wù)包裝成RunnableFuture對(duì)象哟旗,交給子類去執(zhí)行
接口 ScheduledExecutorService 繼承了ExecutorService,增加了定時(shí)任務(wù)相關(guān)的方法
實(shí)現(xiàn)類 ThreadPoolExecutor 繼承了AbstractExecutorService栋操,基礎(chǔ)闸餐、標(biāo)準(zhǔn)的線程池實(shí)現(xiàn)
實(shí)現(xiàn)類 ScheduledThreadPoolExecutor 繼承了ThreadPoolExecutor,實(shí)現(xiàn)ScheduledExecutorService中相關(guān)定時(shí)任務(wù)的方法
工具類 Executors 創(chuàng)建線程池的工具類
1矾芙、Executor

??最上層的執(zhí)行器接口舍沙,定義了執(zhí)行方法execute,參數(shù)類型為Runnable表示可以接收提交到線程池Runnable任務(wù)剔宪,該接口實(shí)現(xiàn)了任務(wù)的提交與任務(wù)的執(zhí)行分離拂铡。



2壹无、ExecutorService

??繼承了Executor接口,ExecutorService擴(kuò)展了功能感帅,定義了關(guān)閉線程池斗锭、提交任務(wù)執(zhí)行任一個(gè)或全部任務(wù)的方法失球,這些方法能產(chǎn)生追蹤一個(gè)或多個(gè)異步任務(wù)進(jìn)度的Future對(duì)象或列表岖是,接口中定義的方法列表如下:

package java.util.concurrent;
import java.util.List;
import java.util.Collection;

public interface ExecutorService extends Executor {
    // 優(yōu)雅關(guān)閉線程池,關(guān)閉前提交的任務(wù)將被執(zhí)行实苞,但不再接受新的任務(wù)
    void shutdown();
    // 馬上關(guān)閉線程池豺撑,所有正在執(zhí)行中的任務(wù)會(huì)被終止,所有正在等待執(zhí)行的任務(wù)不會(huì)被執(zhí)行硬梁,并隨方法返回
    List<Runnable> shutdownNow();
    // 判斷線程池是否已關(guān)閉
    boolean isShutdown();
    // 如果關(guān)閉線程池后前硫,線程池中所有的任務(wù)都已執(zhí)行完成,則返回true荧止;本方法用于判斷線程池是否任務(wù)都執(zhí)行完畢屹电,必須在調(diào)用shutdown()或shutdownNow()后才能使用
    boolean isTerminated();
    // 檢測(cè)線程池是否已關(guān)閉直到所有的任務(wù)都被執(zhí)行結(jié)束,如果還有任務(wù)正在執(zhí)行跃巡,則會(huì)阻塞等待危号,直到發(fā)生超時(shí),或者當(dāng)前線程被中斷
    boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
    // 提交一個(gè)用于執(zhí)行的Callable返回任務(wù)素邪,并返回一個(gè)Future對(duì)象外莲,可以通過(guò)其get方法獲取Callable執(zhí)行結(jié)果
    <T> Future<T> submit(Callable<T> task);
    // 提交一個(gè)用于執(zhí)行的Runnable任務(wù),并返回一個(gè)Future對(duì)象兔朦,執(zhí)行結(jié)果會(huì)放入傳入的result中
    <T> Future<T> submit(Runnable task, T result);
    // 提交一個(gè)Runnable任務(wù)偷线,并返回一個(gè)Future對(duì)象,執(zhí)行結(jié)果為null沽甥,這里返回Future的意義在于可通過(guò)get方法的阻塞控制某些代碼在任務(wù)執(zhí)行結(jié)束之后才執(zhí)行
    Future<?> submit(Runnable task);
    // 執(zhí)行給定的任務(wù)集合声邦,執(zhí)行完畢后,返回結(jié)果集
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
    // 執(zhí)行給定的任務(wù)集合摆舟,執(zhí)行完畢或者超時(shí)后亥曹,其他任務(wù)終止
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;
    // 執(zhí)行給定的任務(wù),任意一個(gè)執(zhí)行成功則返回結(jié)果恨诱,其他任務(wù)終止
    <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;
    // 執(zhí)行給定的任務(wù)媳瞪,任意一個(gè)執(zhí)行成功或者超時(shí)后,則返回結(jié)果照宝,其他任務(wù)終止
    <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}


3蛇受、AbstractExecutorService

??AbstractExecutorServiceExecutorService的實(shí)現(xiàn)類,實(shí)現(xiàn)了ExecutorService接口中定義的submit厕鹃、invokeAny龙巨、invokeAll方法笼呆,執(zhí)行一個(gè)任務(wù)前會(huì)把提交的RunnableCallable任務(wù)通過(guò)newTaskFor方法轉(zhuǎn)化為RunnableFuture對(duì)象旨别,再提交給executor方法執(zhí)行诗赌,executor方法的具體實(shí)現(xiàn)由AbstractExecutorService的子類各自去實(shí)現(xiàn)。

public abstract class AbstractExecutorService implements ExecutorService {

    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }

    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }

    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }
    private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks,
                            boolean timed, long nanos)
        throws InterruptedException, ExecutionException, TimeoutException {
    };
    public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException {
        // implements code
    };
    public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                           long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException {
        // implements code
    };
    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException {
        // implements code
    };
    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                         long timeout, TimeUnit unit)
        throws InterruptedException {
        // implements code
    };


4秸弛、ScheduledExecutorService

??一個(gè)能夠根據(jù)傳入的時(shí)延延遲執(zhí)行和周期參數(shù)定期執(zhí)行任務(wù)的ExecutorService接口铭若,方法定義如下:

public interface ScheduledExecutorService extends ExecutorService {
    // 創(chuàng)建并執(zhí)行一個(gè)一次性任務(wù),過(guò)了延遲時(shí)間就會(huì)被執(zhí)行
    public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);
    public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);
    // 創(chuàng)建一個(gè)周期性任務(wù)递览,過(guò)了給定的初始延遲時(shí)間叼屠,會(huì)被第一次執(zhí)行,執(zhí)行過(guò)程中發(fā)生了異常绞铃,那么任務(wù)就停止
    // 一個(gè)任務(wù)執(zhí)行時(shí)長(zhǎng)超過(guò)了周期時(shí)間镜雨,下一次任務(wù)會(huì)等到該次任務(wù)執(zhí)行結(jié)束之后立即執(zhí)行
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);    
    // 創(chuàng)建一個(gè)周期性任務(wù),過(guò)了給定的初始延遲時(shí)間儿捧,會(huì)被第一次執(zhí)行荚坞,執(zhí)行過(guò)程中發(fā)生了異常,那么任務(wù)就停止
    // 一個(gè)任務(wù)執(zhí)行時(shí)長(zhǎng)超過(guò)了周期時(shí)間菲盾,下一次任務(wù)會(huì)等到該次任務(wù)執(zhí)行結(jié)束的時(shí)間基礎(chǔ)上颓影,計(jì)算執(zhí)行延遲
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);
}

scheduleAtFixedRatescheduleWithFixedDelay的不同如下】
假設(shè)執(zhí)行周期(delay)為3秒,代表每隔3秒執(zhí)行一次任務(wù)懒鉴,任務(wù)是一個(gè)一個(gè)執(zhí)行的诡挂,不會(huì)出現(xiàn)有兩個(gè)不同周期的任務(wù)同時(shí)執(zhí)行的情況,假設(shè)在第10秒開(kāi)始執(zhí)行任務(wù)临谱,任務(wù)執(zhí)行了4秒璃俗,超過(guò)了周期時(shí)間,那么下個(gè)周期的任務(wù)會(huì)在第幾秒開(kāi)始執(zhí)行悉默?
scheduleAtFixedRate:在第14秒就馬上開(kāi)始執(zhí)行城豁。
scheduleWithFixedDelay:在第14秒之后,等3秒到第17秒時(shí)才開(kāi)始執(zhí)行麦牺。

5、ThreadPoolExecutor

??ThreadPoolExecutor是一個(gè)基礎(chǔ)鞭缭、標(biāo)準(zhǔn)的線程池剖膳,繼承了AbstractExecutorService,也是最常用岭辣、直接使用的線程池吱晒,ThreadPoolExecutor構(gòu)造器中主要的參數(shù)有核心線程大小最大線程數(shù)沦童、線程存活時(shí)間仑濒、存活時(shí)間單位叹话、線程阻塞隊(duì)列線程工廠墩瞳、拒絕策略驼壶。

  • 核心線程數(shù)

??如果提交的任務(wù)是通過(guò)核心線程來(lái)執(zhí)行的,則不會(huì)受線程存活時(shí)間的限制喉酌。

  • 最大線程數(shù)

??創(chuàng)建線程有代價(jià)热凹,系統(tǒng)資源寶貴,不可能無(wú)限地創(chuàng)建線程泪电,所以要設(shè)置最大線程數(shù)般妙;最大線程數(shù)是指包含核心線程數(shù)在內(nèi)的所能創(chuàng)建的最大線程數(shù)。

  • 線程存活時(shí)間

??針對(duì)非核心線程設(shè)置的線程存活時(shí)間相速,防止大量非核心線程因阻塞或其他原因?qū)е聢?zhí)行時(shí)間過(guò)長(zhǎng)碟渺,進(jìn)而導(dǎo)致等待執(zhí)行的工作隊(duì)列一直等不到線程來(lái)執(zhí)行。

  • 存活時(shí)間單位

??設(shè)置線程存活時(shí)間時(shí)的時(shí)間單位突诬,有納秒苫拍、微秒、毫秒攒霹、秒怯疤、分、時(shí)催束、天集峦。

  • 線程工廠

??允許通過(guò)傳入的自定義的實(shí)現(xiàn)了ThreadFactory接口的類來(lái)創(chuàng)建線程。

  • 拒絕策略

??當(dāng)提交任務(wù)過(guò)多抠刺,線程池和工作隊(duì)列的容量都無(wú)法執(zhí)行和緩存所有工作任務(wù)時(shí)塔淤,就需要采取一定的策略拒絕提交的任務(wù)。

定義的具體構(gòu)造器如下:

public class ThreadPoolExecutor extends AbstractExecutorService {
    // 使用默認(rèn)線程工廠和拒絕策略的構(gòu)造器速妖,其余參數(shù)指定
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }    
    // 使用默認(rèn)拒絕策略的構(gòu)造器高蜂,其余參數(shù)指定
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }
    // 使用默認(rèn)線程工廠的構(gòu)造器,其余參數(shù)指定
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }
    // 指定所有參數(shù)的構(gòu)造器
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
}

??通常調(diào)用submit方法去提交任務(wù)罕容,接著調(diào)用execute方法執(zhí)行任務(wù)备恤,execute的工作流程如下:
(1)判斷線程池中的核心線程數(shù)是否已滿?若未滿锦秒,則直接創(chuàng)建一個(gè)核心工作線程來(lái)執(zhí)行任務(wù)露泊;否則進(jìn)入(2)。
(2)判斷工作隊(duì)列是否已滿旅择?若未滿惭笑,則將新提交的任務(wù)存儲(chǔ)在工作隊(duì)列中;否則進(jìn)入(3)。
(3)判斷工作線程是否已達(dá)最大數(shù)量限制沉噩?沒(méi)達(dá)到捺宗,則創(chuàng)建一個(gè)新的工作線程來(lái)執(zhí)行任務(wù);否則進(jìn)入(4)川蒙。
(4)執(zhí)行拒絕策略來(lái)處理任務(wù)蚜厉。

【流程圖如下】


【源碼】


【舉例】
??核心線程數(shù)為5,最大線程數(shù)為10(5秒超時(shí))派歌,工作隊(duì)列為3弯囊,現(xiàn)在要提交15個(gè)任務(wù)(假設(shè)幾乎同一時(shí)間提交,忽略提交時(shí)延),每個(gè)任務(wù)的執(zhí)行時(shí)間為6秒,那么線程池執(zhí)行的情況如下:
(1)為提交的前5個(gè)任務(wù)創(chuàng)建核心工作線程嫩海,沒(méi)有超時(shí)時(shí)間,任務(wù)可以執(zhí)行完畢霎烙。
(2)第6到第8個(gè)任務(wù)會(huì)被工作隊(duì)列緩存。
(3)為第9到第13個(gè)任務(wù)創(chuàng)建非核心工作線程蕊连,由于超時(shí)時(shí)間為5秒悬垃,所以這5個(gè)工作線程會(huì)在第5秒時(shí)中斷。
(4)拒絕第14到第15個(gè)提交的任務(wù)甘苍。

【代碼示例】

public class Demo9 {
    public void testCommon(ThreadPoolExecutor threadPoolExecutor) throws Exception {
        // 測(cè)試: 提交15個(gè)執(zhí)行時(shí)間需要3秒的任務(wù)尝蠕,看超過(guò)大小的2個(gè),對(duì)應(yīng)的處理情況
        for (int i = 0; i < 15; i++) {
            int n = i;
            threadPoolExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("開(kāi)始執(zhí)行:" + n);
                        Thread.sleep(3000L);
                        System.err.println("執(zhí)行結(jié)束:" + n);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            System.out.println("任務(wù)提交成功 :" + i);
        }
        // 查看線程數(shù)量载庭,查看隊(duì)列等待數(shù)量
        Thread.sleep(500L);
        System.out.println("當(dāng)前線程池線程數(shù)量為:" + threadPoolExecutor.getPoolSize());
        System.out.println("當(dāng)前線程池等待的數(shù)量為:" + threadPoolExecutor.getQueue().size());
        // 等待15秒看彼,查看線程數(shù)量和隊(duì)列數(shù)量(理論上,會(huì)被超出核心線程數(shù)量的線程自動(dòng)銷毀)
        Thread.sleep(15000L);
        System.out.println("當(dāng)前線程池線程數(shù)量為:" + threadPoolExecutor.getPoolSize());
        System.out.println("當(dāng)前線程池等待的數(shù)量為:" + threadPoolExecutor.getQueue().size());      
    }

    /**
     * 線程池信息: 核心線程數(shù)量5囚聚,最大數(shù)量10靖榕,隊(duì)列大小3,超出核心線程數(shù)量的線程存活時(shí)間:5秒顽铸,指定拒絕策略
     * @throws Exception
     */
    private void threadPoolExecutorTest2() throws Exception {
        // 創(chuàng)建一個(gè) 核心線程數(shù)量為5茁计,最大數(shù)量為10,等待隊(duì)列最大是3 的線程池,也就是最大容納13個(gè)任務(wù)谓松。
        // 默認(rèn)的策略是拋出RejectedExecutionException異常星压,java.util.concurrent.ThreadPoolExecutor.AbortPolicy     
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(3), 
                new RejectedExecutionHandler() {                    
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        System.err.println("有任務(wù)被拒絕執(zhí)行了");
                    }
                });
        testCommon(threadPoolExecutor);
        // 預(yù)計(jì)結(jié)果:
        // 1、5個(gè)任務(wù)直接分配線程開(kāi)始執(zhí)行
        // 2鬼譬、3個(gè)任務(wù)進(jìn)入等待隊(duì)列
        // 3娜膘、隊(duì)列不夠用,臨時(shí)加開(kāi)5個(gè)線程來(lái)執(zhí)行任務(wù)(5秒沒(méi)活干就銷毀)
        // 4拧簸、隊(duì)列和線程池都滿了劲绪,剩下2個(gè)任務(wù)男窟,沒(méi)資源了盆赤,被拒絕執(zhí)行
        // 5贾富、任務(wù)執(zhí)行,5秒后牺六,如果五任務(wù)可執(zhí)行颤枪,銷毀臨時(shí)創(chuàng)建的5個(gè)線程
    }
    
    public static void main(String[] args) throws Exception {
        Demo9 demo = new Demo9();
        demo.threadPoolExecutorTest2();
    }
}

【執(zhí)行結(jié)果】



(1) 如果沒(méi)有指定任務(wù)等待隊(duì)列的長(zhǎng)度,則默認(rèn)為無(wú)界隊(duì)列淑际,不管多少任務(wù)提交都會(huì)被緩存畏纲,這時(shí)其實(shí)拒絕策略已經(jīng)不起作用了,可以直接不指定春缕。

【策略分析】
??在某些場(chǎng)景下盗胀,不急著提交的任務(wù)馬上被執(zhí)行,只要提交的任務(wù)被放入等待執(zhí)行的隊(duì)列中锄贼,由線程池慢慢處理即可票灰,這種情況適合將阻塞隊(duì)列設(shè)置為無(wú)界隊(duì)列(或者容量比較大的隊(duì)列),這樣當(dāng)線程池已滿時(shí)宅荤,提交的任務(wù)會(huì)被緩存到隊(duì)列中而不會(huì)被拒絕執(zhí)行屑迂,因此設(shè)置為無(wú)界隊(duì)列時(shí),不管是否指定拒絕策略冯键,都不會(huì)拒絕惹盼。

【代碼示例】

public class Demo9 {

    /**
     * 測(cè)試: 提交15個(gè)執(zhí)行時(shí)間需要3秒的任務(wù),看線程池的狀況
     * 
     * @param threadPoolExecutor 傳入不同的線程池,看不同的結(jié)果
     * @throws Exception
     */
    public void testCommon(ThreadPoolExecutor threadPoolExecutor) throws Exception {
            // code
    }

    /***
     * 1惫确、線程池信息: 核心線程數(shù)量5手报,最大數(shù)量10,無(wú)界隊(duì)列雕薪,超出核心線程數(shù)量的線程存活時(shí)間: 5秒昧诱,緩存提交任務(wù)的工作隊(duì)列(這里沒(méi)有指定隊(duì)列大小,就是無(wú)界隊(duì)列所袁,可以無(wú)限緩存任務(wù))
     */
    private void threadPoolExecutorTest1() throws Exception {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
        testCommon(threadPoolExecutor);
    }
    public static void main(String[] args) throws Exception {
        Demo9 demo = new Demo9();
        demo.threadPoolExecutorTest1();
    }
}

【執(zhí)行結(jié)果】



(2) 如果任務(wù)提交的頻率不可控不可預(yù)估盏档,也不想堆積緩存而是盡快任務(wù),則
a. 核心線程數(shù)設(shè)為0
b. 線程數(shù)設(shè)一個(gè)比較大的值
c. 用同步隊(duì)列作為阻塞隊(duì)列

【策略解析】
??一般我們不會(huì)把核心線程數(shù)設(shè)置得過(guò)大燥爷,因?yàn)楹诵木€程執(zhí)行完任務(wù)之后是不會(huì)被銷毀而是一直存活的蜈亩,如果核心線程數(shù)設(shè)置過(guò)大,那么當(dāng)某個(gè)時(shí)間點(diǎn)提交了大量的任務(wù)時(shí)前翎,就會(huì)相應(yīng)創(chuàng)建大量的核心線程稚配,當(dāng)這些任務(wù)被執(zhí)行完后,核心線程還存活港华,而后面可能任務(wù)提交的頻率沒(méi)這么高了道川,用不了這么多核心線程,這時(shí)太多核心線程會(huì)占用太多系統(tǒng)資源,系統(tǒng)性能會(huì)受到影響冒萄。
??這時(shí)適合把線程數(shù)量設(shè)大一點(diǎn)臊岸,而核心線程數(shù)不能設(shè)置得太大,某個(gè)時(shí)間點(diǎn)任務(wù)提交頻率又很高尊流,這時(shí)核心線程對(duì)于整個(gè)執(zhí)行任務(wù)的貢獻(xiàn)比例很小帅戒,干脆直接設(shè)置為0即可,這就意味著實(shí)際上執(zhí)行任務(wù)的線程都不會(huì)核心線程崖技,在任務(wù)執(zhí)行完之后線程會(huì)被銷毀逻住,系統(tǒng)資源會(huì)被回收,雖然創(chuàng)建銷毀的代價(jià)也很大迎献,但是跟維護(hù)大量存活的核心線程相比瞎访,還是好得多;而且使用非核心線程使得線程池具有了更好的彈性吁恍,任務(wù)多我就多創(chuàng)建線性装诡,任務(wù)少我就少創(chuàng)建線程,可以滿足任務(wù)提交頻率不可控不定的場(chǎng)景践盼。
??我們不想堆積任務(wù)鸦采,就意味著不應(yīng)該緩存任務(wù),而是直接為任務(wù)創(chuàng)建工作線程咕幻,因此適合用同步隊(duì)列作為阻塞隊(duì)列渔伯,因?yàn)橥疥?duì)列不會(huì)真正緩存任務(wù),而是維護(hù)一組提交任務(wù)的線程肄程,在任務(wù)入隊(duì)offer時(shí)會(huì)失敗锣吼,進(jìn)而觸發(fā)線程池為提交任務(wù)新增工作線程的效果。

【代碼示例】

    /**
     * 4蓝厌、 線程池信息:
     * 核心線程數(shù)量0玄叠,最大數(shù)量Integer.MAX_VALUE,SynchronousQueue隊(duì)列拓提,超出核心線程數(shù)量的線程存活時(shí)間:60秒
     * 
     * @throws Exception
     */
    private void threadPoolExecutorTest4() throws Exception {
        
        // SynchronousQueue读恃,實(shí)際上它不是一個(gè)真正的隊(duì)列,因?yàn)樗粫?huì)為隊(duì)列中元素維護(hù)存儲(chǔ)空間代态。與其他隊(duì)列不同的是寺惫,它維護(hù)一組線程,這些線程在等待著把元素加入或移出隊(duì)列蹦疑。
        // 在使用SynchronousQueue作為工作隊(duì)列的前提下西雀,客戶端代碼向線程池提交任務(wù)時(shí),
        // 而線程池中又沒(méi)有空閑的線程能夠從SynchronousQueue隊(duì)列實(shí)例中取一個(gè)任務(wù)歉摧,
        // 那么相應(yīng)的offer方法調(diào)用就會(huì)失斖щ取(即任務(wù)沒(méi)有被存入工作隊(duì)列)腔呜。
        // 此時(shí),ThreadPoolExecutor會(huì)新建一個(gè)新的工作者線程用于對(duì)這個(gè)入隊(duì)列失敗的任務(wù)進(jìn)行處理(假設(shè)此時(shí)線程池的大小還未達(dá)到其最大線程池大小maximumPoolSize)再悼。

        // 和Executors.newCachedThreadPool()一樣的      
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, 
                new SynchronousQueue<Runnable>());
        testCommon(threadPoolExecutor);
        // 預(yù)計(jì)結(jié)果:
        // 1育谬、 線程池線程數(shù)量為:15,超出數(shù)量的任務(wù)帮哈,其他的進(jìn)入隊(duì)列中等待被執(zhí)行
        // 2、 所有任務(wù)執(zhí)行結(jié)束锰镀,60秒后娘侍,如果無(wú)任務(wù)可執(zhí)行,所有線程全部被銷毀泳炉,池的大小恢復(fù)為0
        Thread.sleep(60000L);
        System.out.println("60秒后憾筏,再看線程池中的數(shù)量:" + threadPoolExecutor.getPoolSize());       
    }

【執(zhí)行結(jié)果】



(3) 如果提交的任務(wù)想定時(shí)執(zhí)行,則適用ScheduledThreadPoolExecutor

【策略分析】
??ScheduledThreadPoolExecutor實(shí)現(xiàn)了ScheduledExecutorService接口花鹅,使用ScheduledThreadPoolExecutor能滿足定時(shí)任務(wù)的需求氧腰;需要注意的地方是,如果定時(shí)任務(wù)中同一時(shí)間要執(zhí)行的任務(wù)很多刨肃,則應(yīng)該把線程數(shù)設(shè)大一點(diǎn)古拴,因?yàn)闉榱吮WC設(shè)定的時(shí)間一到時(shí)線程池中必須有可以執(zhí)行任務(wù)的線程;ScheduledThreadPoolExecutor中的線程全部都是核心線程真友,因?yàn)槭褂枚〞r(shí)任務(wù)的場(chǎng)景意味著任務(wù)被不斷周期性執(zhí)行黄痪,并且不必立即執(zhí)行,任務(wù)提交的頻率相對(duì)穩(wěn)定盔然,所以使用核心線程利用率高桅打,相對(duì)劃算。
??ScheduledThreadPoolExecutor沒(méi)有提供指定阻塞隊(duì)列的構(gòu)造參數(shù)愈案,是因?yàn)槠涞讓訉?shí)現(xiàn)的原理依賴于DelayWorkQueue延時(shí)隊(duì)列挺尾,任務(wù)被提交到延時(shí)隊(duì)列之后,必須在設(shè)定的延時(shí)時(shí)間后才能取出任務(wù)給線程執(zhí)行站绪。

【代碼示例:只延遲不定期執(zhí)行的任務(wù)】

    /**
     * 5遭铺、 定時(shí)執(zhí)行線程池信息:3秒后執(zhí)行,一次性任務(wù)恢准,到點(diǎn)就執(zhí)行 <br/>
     * 核心線程數(shù)量5掂僵,最大數(shù)量Integer.MAX_VALUE,DelayedWorkQueue延時(shí)隊(duì)列顷歌,超出核心線程數(shù)量的線程存活時(shí)間:0秒
     * 
     * @throws Exception
     */ 
    private void threadPoolExecutorTest5() throws Exception {
        // 和Executors.newScheduledThreadPool()一樣的
        ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(5);
        threadPoolExecutor.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("任務(wù)被執(zhí)行锰蓬,現(xiàn)在時(shí)間:" + System.currentTimeMillis());
            }
        }, 3000, TimeUnit.MILLISECONDS);
        System.out.println(
                "定時(shí)任務(wù),提交成功眯漩,時(shí)間是:" + System.currentTimeMillis() + ", 當(dāng)前線程池中線程數(shù)量:" + threadPoolExecutor.getPoolSize());
        // 預(yù)計(jì)結(jié)果:任務(wù)在3秒后被執(zhí)行一次
    }

【執(zhí)行結(jié)果】


【代碼示例:延遲定期的任務(wù)】
??定期執(zhí)行任務(wù)有兩種形式芹扭,一種是使用scheduleAtFixedRate麻顶,假設(shè)某次定時(shí)任務(wù)執(zhí)行的時(shí)間超過(guò)了周期時(shí)間,則下一次執(zhí)行會(huì)在上一次執(zhí)行結(jié)束之后馬上執(zhí)行舱卡;另一種是使用scheduleWithFixedDelay辅肾,不管定時(shí)任務(wù)執(zhí)行的時(shí)間是否超過(guò)周期時(shí)間,下一次執(zhí)行都會(huì)在上一次執(zhí)行結(jié)束之后等待固定的周期時(shí)間之后才開(kāi)始執(zhí)行轮锥。如果周期時(shí)間大于任務(wù)執(zhí)行時(shí)間矫钓,則兩種定期任務(wù)的的效果都一樣。
??下面的代碼中舍杜,設(shè)置了延遲時(shí)間為2秒新娜,定時(shí)周期為1秒,每個(gè)任務(wù)的執(zhí)行時(shí)間為3秒既绩,這就意味著概龄,當(dāng)一個(gè)周期過(guò)去時(shí)要開(kāi)始新的周期時(shí),上個(gè)周期的任務(wù)還沒(méi)被執(zhí)行完畢饲握,兩種方式都不會(huì)在上個(gè)周期的任務(wù)還沒(méi)執(zhí)行完畢是又開(kāi)始執(zhí)行新的周期任務(wù)私杜;對(duì)于scheduleAtFixedRate方式的線程池來(lái)說(shuō),會(huì)在3秒任務(wù)執(zhí)行完畢后救欧,馬上執(zhí)行新的周期任務(wù)衰粹,對(duì)于scheduleWithFixedDelay方式的線程池,會(huì)在3秒任務(wù)執(zhí)行完畢之后笆怠,再等待1秒鐘才開(kāi)始執(zhí)行新的周期任務(wù)寄猩。

    /**
     * 6、 定時(shí)執(zhí)行線程池信息:線程固定數(shù)量5 骑疆,<br/>
     * 核心線程數(shù)量5田篇,最大數(shù)量Integer.MAX_VALUE,DelayedWorkQueue延時(shí)隊(duì)列箍铭,超出核心線程數(shù)量的線程存活時(shí)間:0秒
     * 
     * @throws Exception
     */
    private void threadPoolExecutorTest6() throws Exception {
        ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(5);
        // 周期性執(zhí)行某一個(gè)任務(wù)泊柬,線程池提供了兩種調(diào)度方式,這里單獨(dú)演示一下诈火。測(cè)試場(chǎng)景一樣兽赁。
        // 測(cè)試場(chǎng)景:提交的任務(wù)需要3秒才能執(zhí)行完畢±涫兀看兩種不同調(diào)度方式的區(qū)別
        // 效果1: 提交后刀崖,2秒后開(kāi)始第一次執(zhí)行,之后每間隔1秒拍摇,固定執(zhí)行一次(如果發(fā)現(xiàn)上次執(zhí)行還未完畢亮钦,則等待完畢,完畢后立刻執(zhí)行)充活。
        // 也就是說(shuō)這個(gè)代碼中是蜂莉,3秒鐘執(zhí)行一次(計(jì)算方式:每次執(zhí)行三秒蜡娶,間隔時(shí)間1秒,執(zhí)行結(jié)束后馬上開(kāi)始下一次執(zhí)行映穗,無(wú)需等待)
        threadPoolExecutor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("任務(wù)-1 被執(zhí)行窖张,現(xiàn)在時(shí)間:" + System.currentTimeMillis());
            }
        }, 2000, 1000, TimeUnit.MILLISECONDS);

        // 效果2:提交后,2秒后開(kāi)始第一次執(zhí)行蚁滋,之后每間隔1秒宿接,固定執(zhí)行一次(如果發(fā)現(xiàn)上次執(zhí)行還未完畢,則等待完畢辕录,等上一次執(zhí)行完畢后再開(kāi)始計(jì)時(shí)睦霎,等待1秒)。
        // 也就是說(shuō)這個(gè)代碼鐘的效果看到的是:4秒執(zhí)行一次踏拜。 (計(jì)算方式:每次執(zhí)行3秒,間隔時(shí)間1秒低剔,執(zhí)行完以后再等待1秒速梗,所以是 3+1)
        threadPoolExecutor.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("任務(wù)-2 被執(zhí)行,現(xiàn)在時(shí)間:" + System.currentTimeMillis());
            }
        }, 2000, 1000, TimeUnit.MILLISECONDS);  
}

【執(zhí)行結(jié)果:?jiǎn)为?dú)執(zhí)行scheduleAtFixedRate方式表現(xiàn)出來(lái)的效果是間隔3秒襟齿;
??????單獨(dú)執(zhí)行scheduleWithFixedDelay方式表現(xiàn)出來(lái)的效果是間隔4秒】




(4)終止提交任務(wù)

??有時(shí)候姻锁,我們?cè)诔绦驁?zhí)行的某個(gè)節(jié)點(diǎn)終止線程池,終止線程池之后不會(huì)再接收新的任務(wù)猜欺,當(dāng)線程池和緩存隊(duì)列已滿或者線程池已終止時(shí)位隶,都會(huì)執(zhí)行拒絕策略。如果想在終止時(shí)开皿,讓終止前已經(jīng)提交的任務(wù)繼續(xù)執(zhí)行完畢涧黄,則使用shutdown方法;如果想讓所有任務(wù)都終止赋荆,并且正在執(zhí)行的任務(wù)也要嘗試中斷笋妥,則使用shutdownNow方法。

【代碼示例:使用shutdown關(guān)閉線程池】

    /**
     * 7窄潭、 終止線程:線程池信息: 核心線程數(shù)量5春宣,最大數(shù)量10,隊(duì)列大小3嫉你,超出核心線程數(shù)量的線程存活時(shí)間:5秒月帝, 指定拒絕策略的
     * 
     * @throws Exception
     */
    private void threadPoolExecutorTest7() throws Exception {
        // 創(chuàng)建一個(gè) 核心線程數(shù)量為5,最大數(shù)量為10,等待隊(duì)列最大是3 的線程池幽污,也就是最大容納13個(gè)任務(wù)嚷辅。
        // 默認(rèn)的策略是拋出RejectedExecutionException異常,java.util.concurrent.ThreadPoolExecutor.AbortPolicy
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(3), new RejectedExecutionHandler() {
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        System.err.println("有任務(wù)被拒絕執(zhí)行了");
                    }
                });
        // 測(cè)試: 提交15個(gè)執(zhí)行時(shí)間需要3秒的任務(wù)距误,看超過(guò)大小的2個(gè)潦蝇,對(duì)應(yīng)的處理情況
        for (int i = 0; i < 15; i++) {
            int n = i;
            threadPoolExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("開(kāi)始執(zhí)行:" + n);
                        Thread.sleep(3000L);
                        System.err.println("執(zhí)行結(jié)束:" + n);
                    } catch (InterruptedException e) {
                        System.out.println("異常:" + e.getMessage());
                    }
                }
            });
            System.out.println("任務(wù)提交成功 :" + i);
        }
        // 1秒后終止線程池
        Thread.sleep(1000L);
        threadPoolExecutor.shutdown();
        // 再次提交提示失敗
        threadPoolExecutor.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("追加一個(gè)任務(wù)");
            }
        });
        // 結(jié)果分析
        // 1款熬、 10個(gè)任務(wù)被執(zhí)行,3個(gè)任務(wù)進(jìn)入隊(duì)列等待攘乒,2個(gè)任務(wù)被拒絕執(zhí)行
        // 2贤牛、調(diào)用shutdown后,不接收新的任務(wù)则酝,等待13任務(wù)執(zhí)行結(jié)束
        // 3殉簸、 追加的任務(wù)在線程池關(guān)閉后,無(wú)法再提交沽讹,會(huì)被拒絕執(zhí)行
    }

【執(zhí)行結(jié)果】


【代碼示例:使用shutdownNow關(guān)閉線程池】

    /**
     * 8般卑、 立刻終止線程:線程池信息: 核心線程數(shù)量5,最大數(shù)量10爽雄,隊(duì)列大小3蝠检,超出核心線程數(shù)量的線程存活時(shí)間:5秒, 指定拒絕策略的
     * 
     * @throws Exception
     */
    private void threadPoolExecutorTest8() throws Exception {
        // 創(chuàng)建一個(gè) 核心線程數(shù)量為5挚瘟,最大數(shù)量為10,等待隊(duì)列最大是3 的線程池叹谁,也就是最大容納13個(gè)任務(wù)。
        // 默認(rèn)的策略是拋出RejectedExecutionException異常乘盖,java.util.concurrent.ThreadPoolExecutor.AbortPolicy
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(3), new RejectedExecutionHandler() {
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        System.err.println("有任務(wù)被拒絕執(zhí)行了");
                    }
                });
        // 測(cè)試: 提交15個(gè)執(zhí)行時(shí)間需要3秒的任務(wù)焰檩,看超過(guò)大小的2個(gè),對(duì)應(yīng)的處理情況
        for (int i = 0; i < 15; i++) {
            int n = i;
            threadPoolExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("開(kāi)始執(zhí)行:" + n);
                        Thread.sleep(3000L);
                        System.err.println("執(zhí)行結(jié)束:" + n);
                    } catch (InterruptedException e) {
                        System.out.println("異常:" + e.getMessage());
                    }
                }
            });
            System.out.println("任務(wù)提交成功 :" + i);
        }
        // 1秒后終止線程池
        Thread.sleep(1000L);
        List<Runnable> shutdownNow = threadPoolExecutor.shutdownNow();
        // 再次提交提示失敗
        threadPoolExecutor.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("追加一個(gè)任務(wù)");
            }
        });
        System.out.println("未結(jié)束的任務(wù)有:" + shutdownNow.size());

        // 結(jié)果分析
        // 1订框、 10個(gè)任務(wù)被執(zhí)行析苫,3個(gè)任務(wù)進(jìn)入隊(duì)列等待,2個(gè)任務(wù)被拒絕執(zhí)行
        // 2穿扳、調(diào)用shutdownnow后衩侥,隊(duì)列中的3個(gè)線程不再執(zhí)行,10個(gè)線程被終止
        // 3矛物、 追加的任務(wù)在線程池關(guān)閉后顿乒,無(wú)法再提交,會(huì)被拒絕執(zhí)行
    }

【執(zhí)行結(jié)果】



6泽谨、Executors

??Executors是一個(gè)工具類璧榄,提供了創(chuàng)建各種線程池的方法,下面的代碼是等同的:
(1) Executors.newFixedThreadPool(int nThreads) = ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
【效果】
??創(chuàng)建了一個(gè)指定核心線程數(shù)吧雹,無(wú)非核心工作線程骨杂,任務(wù)緩存隊(duì)列無(wú)限的線程池。
【源碼】




(2)Executors.newCachedThreadPool() = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>())
【效果】
??創(chuàng)建了一個(gè)核心線程為0雄卷,最大線程數(shù)為Integer最大值搓蚪,使用同步隊(duì)列的線程池。
【源碼】



(3)Executors.newScheduledThreadPool(int corePoolSize) = new ScheduledThreadPoolExecutor(int corePoolSize)
【效果】
??創(chuàng)建一個(gè)指定核心線程數(shù)的定期任務(wù)線程池丁鹉。
【源碼】


三妒潭、總結(jié)對(duì)比一覽

1悴能、幾種常用線程池對(duì)比
線程池創(chuàng)建 核心線程數(shù) 最大線程數(shù) 隊(duì)列 適用場(chǎng)景
Executors.newFixedThreadPool(int nThreads) nThread nThread 阻塞隊(duì)列,大小無(wú)界 任務(wù)提交頻率穩(wěn)定
Executors.newCachedThreadPool() 0 Integer.MAX_VALUE 同步隊(duì)列雳灾,不緩存任務(wù) 任務(wù)提交頻率不定
Executors.newScheduledThreadPool(int corePoolSize) corePoolSize corePoolSize 無(wú)界延時(shí)隊(duì)列 定時(shí)任務(wù)


2漠酿、兩種定時(shí)任務(wù)對(duì)比
方法 策略
scheduleAtFixedRate 如果任務(wù)執(zhí)行時(shí)間超過(guò)定時(shí)周期,則下一次任務(wù)執(zhí)行在上一次任務(wù)執(zhí)行結(jié)束之后馬上開(kāi)始
scheduleWithFixedDelay 如果任務(wù)執(zhí)行時(shí)間超過(guò)定時(shí)周期谎亩,則下一次任務(wù)執(zhí)行在上一次任務(wù)執(zhí)行結(jié)束之后再等待一個(gè)周期的時(shí)間再開(kāi)始


3炒嘲、兩種關(guān)閉線程池對(duì)比
方法 是否允許提交新任務(wù) 是否執(zhí)行等待隊(duì)列中的任務(wù) 是否嘗試中斷正在執(zhí)行的任務(wù)
shutdown
shutdownNow
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市匈庭,隨后出現(xiàn)的幾起案子夫凸,更是在濱河造成了極大的恐慌,老刑警劉巖阱持,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件夭拌,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡衷咽,警方通過(guò)查閱死者的電腦和手機(jī)鸽扁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)兵罢,“玉大人献烦,你說(shuō)我怎么就攤上這事滓窍÷舸剩” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵吏夯,是天一觀的道長(zhǎng)此蜈。 經(jīng)常有香客問(wèn)我,道長(zhǎng)噪生,這世上最難降的妖魔是什么裆赵? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮跺嗽,結(jié)果婚禮上战授,老公的妹妹穿的比我還像新娘。我一直安慰自己桨嫁,他們只是感情好植兰,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著璃吧,像睡著了一般楣导。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上畜挨,一...
    開(kāi)封第一講書(shū)人閱讀 49,829評(píng)論 1 290
  • 那天筒繁,我揣著相機(jī)與錄音噩凹,去河邊找鬼。 笑死毡咏,一個(gè)胖子當(dāng)著我的面吹牛驮宴,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播血当,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼幻赚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了臊旭?” 一聲冷哼從身側(cè)響起落恼,我...
    開(kāi)封第一講書(shū)人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎离熏,沒(méi)想到半個(gè)月后佳谦,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡滋戳,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年钻蔑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奸鸯。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡咪笑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出娄涩,到底是詐尸還是另有隱情窗怒,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布蓄拣,位于F島的核電站扬虚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏球恤。R本人自食惡果不足惜辜昵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望咽斧。 院中可真熱鬧堪置,春花似錦、人聲如沸张惹。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)诵叁。三九已至雁竞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背碑诉。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工彪腔, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人进栽。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓德挣,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親快毛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子格嗅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

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