線程池


Java線程 一般采用new Thread().start();的方式開啟一個新的線程池實例。但是這樣的會無節(jié)制的創(chuàng)建線程俱箱、回收線程奏窑,造成頻繁的GC。

線程池的由來就是為了解決此問題的舵鳞。線程池旨在線程的復(fù)用,這就可以節(jié)約我們用以往的方式創(chuàng)建線程和銷毀所消耗的時間琢蛤,減少線程頻繁調(diào)度的開銷蜓堕,從而節(jié)約系統(tǒng)資源,提高系統(tǒng)吞吐量博其。

在Java線程池概念中套才,ExecutorService,它是一個接口慕淡,其實如果要從真正意義上來說背伴,它可以叫做線程池的服務(wù),因為它提供了眾多接口api來控制線程池中的線程,而真正意義上的線程池就是:ThreadPoolExecutor傻寂,它實現(xiàn)了ExecutorService接口息尺,并封裝了一系列的api使得它具有線程池的特性,其中包括工作隊列疾掰、核心線程數(shù)搂誉、最大線程數(shù)等。

ThreadPoolExecutor

ThreadPoolExecutor參數(shù)

  • int corePoolSize ——線程池中核心線程的數(shù)量静檬。
  • int maximumPoolSize ——線程池中最大線程數(shù)量炭懊。
  • long keepAliveTime——非核心線程的超時時長,當(dāng)系統(tǒng)中非核心線程閑置時間超過keepAliveTime之后拂檩,則會被回收侮腹。如果ThreadPoolExecutor的allowCoreThreadTimeOut屬性設(shè)置為true,則該參數(shù)也表示核心線程的超時時長广恢。
  • TimeUnit unit ——時間單位凯旋,有納秒、微秒钉迷、毫秒、秒钠署、分糠聪、時、天等谐鼎。
  • BlockingQueue< Runnable > workQueue ——線程池中的任務(wù)隊列舰蟆,該隊列主要用來存儲已經(jīng)被提交但是尚未執(zhí)行的任務(wù)。
  • ThreadFactory threadFactory —— 線程工廠狸棍,為了給線程池提供創(chuàng)建線程的功能身害。
  • RejectedExecutionHandler handler——拒絕策略,當(dāng)線程無法執(zhí)行新任務(wù)時(一般是由于線程池中的線程數(shù)量已經(jīng)達到最大數(shù)或者線程池關(guān)閉導(dǎo)致的)草戈,默認情況下塌鸯,當(dāng)線程池?zé)o法處理新線程時,會拋出一個RejectedExecutionException唐片。

任務(wù)隊列

用于保存等待執(zhí)行的任務(wù)的阻塞隊列丙猬,可以選擇以下幾個:

  • ArrayBlockingQueue:基于數(shù)組的阻塞隊列,按照FIFO原則進行排序费韭。
  • LinkedBlockingQueue:基于鏈表的阻塞隊列茧球,按照FIFO原則對元素進行排序。
  • SynchronousQueue:一個不儲存元素的阻塞隊列星持,每一個插入操作必須等到另外一個線程調(diào)用移除操作抢埋,否則插入操作一直處于阻塞狀態(tài)。
  • PriorityBlockingQueue:一個具有優(yōu)先級的無限阻塞隊列。

任務(wù)拒絕策略

  • ThreadPoolExecutor.AbortPolicy:丟棄任務(wù)并拋出RejectedExecutionException異常揪垄。
  • ThreadPoolExecutor.DiscardPolicy:也是丟棄任務(wù)穷吮,但是不拋出異常。
  • ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務(wù)福侈,然后重新嘗試執(zhí)行任務(wù)(重復(fù)此過程)
  • ThreadPoolExecutor.CallerRunsPolicy:由調(diào)用線程處理該任務(wù)

ThreadPoolExecutor工作規(guī)則

  • 如果線程池中的線程數(shù)未達到核心線程數(shù)酒来,則會立馬啟用一個核心線程去執(zhí)行。
  • 如果線程池中的線程數(shù)已經(jīng)達到核心線程數(shù)肪凛,且workQueue未滿堰汉,則將新線程放入workQueue中等待執(zhí)行。
  • 如果線程池中的線程數(shù)已經(jīng)達到核心線程數(shù)且workQueue已滿伟墙,但未超過非核心線程數(shù)翘鸭,則開啟一個非核心線程來執(zhí)行任務(wù)。
  • 如果線程池中的線程數(shù)已經(jīng)超過非核心線程數(shù)戳葵,則拒絕執(zhí)行該任務(wù)就乓。

注:核心線程、非核心線程和線程隊列拱烁,是三個概念哦生蚁,別混為一談。

ThreadPoolExecutor封裝案例

/**
 * 類描述:線程池管理器
 *
 * @author jinzifu
 * @Email jinzifu123@163.com
 * @date 2017/12/7 1150
 */

public class ThreadPoolManager {
    /**
     * 核心線程的數(shù)量戏自。當(dāng)前設(shè)備可用處理器核心數(shù)*2 + 1,能夠讓cpu的效率得到最大程度執(zhí)行邦投。
     */
    private int corePoolSize;
    /**
     * 最大線程數(shù)量,表示當(dāng)緩沖隊列滿的時候能繼續(xù)容納的等待任務(wù)的數(shù)量擅笔。
     */
    private int maximumPoolSize;
    /**
     * 非核心線程的超時時長志衣,當(dāng)系統(tǒng)中非核心線程閑置時間超過keepAliveTime之后,則會被回收猛们。
     * 如果ThreadPoolExecutor的allowCoreThreadTimeOut屬性設(shè)置為true念脯,則該參數(shù)也表示核心線程的超時時長。
     */
    private long keepAliveTime = 1;
    /**
     * 時間單位弯淘,有納秒绿店、微秒、毫秒耳胎、秒惯吕、分、時怕午、天等废登。
     */
    private TimeUnit timeUnit = TimeUnit.HOURS;
    private ThreadPoolExecutor threadPoolExecutor;
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();

    private ThreadPoolManager() {
        init();
    }

    private static class SingleClass {
        private static final ThreadPoolManager THREAD_POOL_MANAGER = new ThreadPoolManager();
    }

    /**
     * 靜態(tài)內(nèi)部類單例模式
     *
     * @return
     */
    public static ThreadPoolManager getInstance() {
        return SingleClass.THREAD_POOL_MANAGER;
    }

    /**
     * 配置線程池屬性
     * 部分參考AsyncTask的配置設(shè)計
     */
    private void init() {
        corePoolSize = CPU_COUNT + 1;
        maximumPoolSize = CPU_COUNT * 2 + 1;
        threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                timeUnit,
                new LinkedBlockingDeque<Runnable>(),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
    }

    /**
     * 執(zhí)行任務(wù)
     *
     * @param runnable
     */
    public void execute(Runnable runnable) {
        if (runnable != null) {
            threadPoolExecutor.execute(runnable);
        }
    }

    /**
     * 從線程池移除任務(wù)
     *
     * @param runnable
     */
    public void remove(Runnable runnable) {
        if (runnable != null) {
            threadPoolExecutor.remove(runnable);
        }
    }
}

FixedThreadPool

   /**
     * Creates a thread pool that reuses a fixed number of threads
     * operating off a shared unbounded queue.  At any point, at most
     * {@code nThreads} threads will be active processing tasks.
     * If additional tasks are submitted when all threads are active,
     * they will wait in the queue until a thread is available.
     * If any thread terminates due to a failure during execution
     * prior to shutdown, a new one will take its place if needed to
     * execute subsequent tasks.  The threads in the pool will exist
     * until it is explicitly {@link ExecutorService#shutdown shutdown}.
     *
     * @param nThreads the number of threads in the pool
     * @return the newly created thread pool
     * @throws IllegalArgumentException if {@code nThreads <= 0}
     */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

從方法注釋和參數(shù)配置來看,核心線程數(shù)和最大線程數(shù)相同(意味著沒有非核心線程郁惜,且線程池處于北ぞ啵活狀態(tài)以等待任務(wù)甲锡,除非調(diào)用shutdown shutdown關(guān)閉線程隊列中的任務(wù)),超過核心線程數(shù)的任務(wù)都要進入線程隊列中等待羽戒,當(dāng)核心線程處于閑置狀態(tài)時就繼續(xù)執(zhí)行隊列里的任務(wù)缤沦。線程池隊列是沒有參數(shù),說明隊列長度是默認的Integer.MAX_VALUE(2的31次方減1)易稠。

特點:固定設(shè)置線程數(shù)缸废,快速響應(yīng)。

外部調(diào)用示例:

 ExecutorService executorService = Executors.newFixedThreadPool(3);
                executorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 20; i++) {
                            try {
                                Thread.sleep(1000);
                                Log.d(TAG, "" + i);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                });

SingleThreadExecutor

 /**
     * Creates an Executor that uses a single worker thread operating
     * off an unbounded queue. (Note however that if this single
     * thread terminates due to a failure during execution prior to
     * shutdown, a new one will take its place if needed to execute
     * subsequent tasks.)  Tasks are guaranteed to execute
     * sequentially, and no more than one task will be active at any
     * given time. Unlike the otherwise equivalent
     * {@code newFixedThreadPool(1)} the returned executor is
     * guaranteed not to be reconfigurable to use additional threads.
     *
     * @return the newly created single-threaded Executor
     */
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

從方法注釋上看驶社,和FixedThreadPool幾乎一樣企量,只不過該線程池的固定線程個數(shù)是1。這樣的好處是避免了線程間的同步問題亡电。

CachedThreadPool

  /**
     * Creates a thread pool that creates new threads as needed, but
     * will reuse previously constructed threads when they are
     * available.  These pools will typically improve the performance
     * of programs that execute many short-lived asynchronous tasks.
     * Calls to {@code execute} will reuse previously constructed
     * threads if available. If no existing thread is available, a new
     * thread will be created and added to the pool. Threads that have
     * not been used for sixty seconds are terminated and removed from
     * the cache. Thus, a pool that remains idle for long enough will
     * not consume any resources. Note that pools with similar
     * properties but different details (for example, timeout parameters)
     * may be created using {@link ThreadPoolExecutor} constructors.
     *
     * @return the newly created thread pool
     */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

從方法注釋里看届巩,該線程池沒有核心線程,所有非核心線程數(shù)就是最大線程數(shù)份乒。由于最大線程數(shù)為無限大恕汇,所以每當(dāng)我們添加一個新任務(wù)進來的時候,如果線程池中有空閑的線程或辖,則由該空閑的線程執(zhí)行新任務(wù)瘾英,如果沒有空閑線程,則創(chuàng)建新線程來執(zhí)行任務(wù)颂暇。根據(jù)CachedThreadPool的特點方咆,我們可以在有大量任務(wù)請求的時候使用CachedThreadPool,因為當(dāng)CachedThreadPool中沒有新任務(wù)的時候蟀架,它里邊所有的線程都會因為超時(60秒)而被終止。

這里它使用了SynchronousQueue作為線程隊列榆骚。

特點:CachedTreadPool一個最大的優(yōu)勢是它可以根據(jù)程序的運行情況自動來調(diào)整線程池中的線程數(shù)量片拍。

ScheduledThreadPool

   /**
     * Creates a new {@code ScheduledThreadPoolExecutor} with the
     * given core pool size.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @throws IllegalArgumentException if {@code corePoolSize < 0}
     */
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }

從方法注釋里可以看到,它的核心線程數(shù)量是固定的(我們在構(gòu)造的時候傳入的)妓肢,但是非核心線程是無窮大捌省,當(dāng)非核心線程閑置時,則會被立即回收碉钠。

ScheduledExecutorService擴展了ExecutorService接口纲缓,提供時間排程的功能。

public interface ScheduledExecutorService extends ExecutorService {

    /**
     * Creates and executes a one-shot action that becomes enabled
     * after the given delay.
     * 翻譯:創(chuàng)建并執(zhí)行在給定延遲后啟用的一次性操作喊废。
     */
    public ScheduledFuture<?> schedule(Runnable command,
                                     long delay, TimeUnit unit);

    /**
     * Creates and executes a ScheduledFuture that becomes enabled after the
     * given delay.
     * 翻譯:創(chuàng)建并執(zhí)行在給定延遲后啟用的操作三妈。
     */
    public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                           long delay, TimeUnit unit);

    /**
     * Creates and executes a periodic action that becomes enabled first
     * after the given initial delay, and subsequently with the given
     * period; that is, executions will commence after
     * 參數(shù)initialDelay:表示第一次延時的時間
     * 參數(shù)period:表示任務(wù)執(zhí)行開始后距下一個任務(wù)開始執(zhí)行的時間間隔柑司。
     */
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);

    /**
     * Creates and executes a periodic action that becomes enabled first
     * after the given initial delay, and subsequently with the
     * given delay between the termination of one execution and the
     * commencement of the next.
     * 參數(shù)initialDelay:表示第一次延時的時間
     * 參數(shù)delay:表示任務(wù)執(zhí)行結(jié)束后距下一個任務(wù)開始執(zhí)行的時間間隔
     */
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);

}

情景代碼測試1executorService.scheduleWithFixedDelay(new MyRunnable(5), 1, 3, TimeUnit.SECONDS);

class MyRunnable implements Runnable {
        private int count;
        final int[] i = {0};

        public MyRunnable(int i) {
            this.count = i;
        }

        @Override
        public void run() {
            try {
                i[0] = i[0] + 1;
                if (i[0] < count) {
                    Log.d(TAG, "開始第" + i[0] + "次打印");
                    Thread.sleep(1000);
                    Log.d(TAG, "結(jié)束第" + i[0] + "次打印");
                } else {
                    Log.d(TAG, "停止延時執(zhí)行...");
                    executorService.shutdownNow();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

Log日志如下

12-07 14:24:41.556 9263-9263 D/ThreadFragment: 開始延時執(zhí)行...
12-07 14:24:42.557 9263-9353 D/ThreadFragment: 開始第1次打印
12-07 14:24:43.559 9263-9353 D/ThreadFragment: 結(jié)束第1次打印
12-07 14:24:46.561 9263-9353 D/ThreadFragment: 開始第2次打印
12-07 14:24:47.562 9263-9353 D/ThreadFragment: 結(jié)束第2次打印
12-07 14:24:50.564 9263-9353 D/ThreadFragment: 開始第3次打印
12-07 14:24:51.567 9263-9353 D/ThreadFragment: 結(jié)束第3次打印
12-07 14:24:54.569 9263-9353 D/ThreadFragment: 開始第4次打印
12-07 14:24:55.570 9263-9353 D/ThreadFragment: 結(jié)束第4次打印
12-07 14:24:58.572 9263-9353 D/ThreadFragment: 停止延時執(zhí)行...

從log日志可以看出:

  • 14:24:41到14:24:42是延遲1秒啟動的。
  • 結(jié)束第1次打印時間與開始第2次打印時間是相隔3秒,驗證了每一次執(zhí)行終止和下一次執(zhí)行開始之間都存在給定的延遲delay么翰。

情景代碼測試2executorService.scheduleAtFixedRate(new MyRunnable(5), 1, 3, TimeUnit.SECONDS);

Log日志如下(MyRunnable內(nèi)代碼一樣)

12-07 14:23:21.371 8012-8012 D/ThreadFragment: 開始延時執(zhí)行...
12-07 14:23:22.373 8012-8115 D/ThreadFragment: 開始第1次打印
12-07 14:23:23.374 8012-8115 D/ThreadFragment: 結(jié)束第1次打印
12-07 14:23:25.373 8012-8115 D/ThreadFragment: 開始第2次打印
12-07 14:23:26.375 8012-8115 D/ThreadFragment: 結(jié)束第2次打印
12-07 14:23:28.374 8012-8115 D/ThreadFragment: 開始第3次打印
12-07 14:23:29.375 8012-8115 D/ThreadFragment: 結(jié)束第3次打印
12-07 14:23:31.374 8012-8115 D/ThreadFragment: 開始第4次打印
12-07 14:23:32.376 8012-8115 D/ThreadFragment: 結(jié)束第4次打印
12-07 14:23:34.372 8012-8115 D/ThreadFragment: 停止延時執(zhí)行...

從log日志可以看出:

  • 14:23:21到14:23:22是延遲1秒啟動的坦康。
  • 開始第1次打印時間與開始第2次打印時間是period = 3秒。

注意:通過ScheduledExecutorService執(zhí)行的周期任務(wù),如果任務(wù)執(zhí)行過程中拋出了異常雷厂,那么過ScheduledExecutorService就會停止執(zhí)行任務(wù),且也不會再周期地執(zhí)行該任務(wù)了叠殷。所以你如果想保住任務(wù)都一直被周期執(zhí)行改鲫,那么catch一切可能的異常。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末林束,一起剝皮案震驚了整個濱河市像棘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌诊县,老刑警劉巖讲弄,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異依痊,居然都是意外死亡避除,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門胸嘁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瓶摆,“玉大人,你說我怎么就攤上這事性宏∪壕” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵毫胜,是天一觀的道長书斜。 經(jīng)常有香客問我,道長酵使,這世上最難降的妖魔是什么荐吉? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮口渔,結(jié)果婚禮上样屠,老公的妹妹穿的比我還像新娘。我一直安慰自己缺脉,他們只是感情好痪欲,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著攻礼,像睡著了一般业踢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秘蛔,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天陨亡,我揣著相機與錄音傍衡,去河邊找鬼。 笑死负蠕,一個胖子當(dāng)著我的面吹牛蛙埂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播遮糖,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼绣的,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了欲账?” 一聲冷哼從身側(cè)響起屡江,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎赛不,沒想到半個月后惩嘉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡踢故,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年文黎,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片殿较。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡耸峭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出淋纲,到底是詐尸還是另有隱情劳闹,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布洽瞬,位于F島的核電站本涕,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏伙窃。R本人自食惡果不足惜偏友,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望对供。 院中可真熱鬧,春花似錦氛濒、人聲如沸产场。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽京景。三九已至,卻和暖如春骗奖,著一層夾襖步出監(jiān)牢的瞬間确徙,已是汗流浹背醒串。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鄙皇,地道東北人芜赌。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像伴逸,于是被迫代替她去往敵國和親缠沈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

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