JDK Executor框架

Executor

基於生產——消費者模式,提交任務相當於生產者,執行任務的線程相當於消費者。如果要在程序中實現生產者——消費者,那麼最簡單的方式就是使用Executor。
線程池中的兩個重要角色是工作隊列(Work Queue)和工作線程(Worker Thread)。生產者向工作隊列中添加任務,消費者(工作線程)從工作隊列中獲取任務並執行,任務執行完成後,返回線程到線程池。
在這裏插入圖片描述

  • Executor 最頂層的類,解耦Runnable的提交和執行
  • ExecutorService 提供生命週期方法shutdown(),shutdownNow()…,和Callable任務提交
  • ScheduledExecutorService 對ExecutorService擴展了定時調用
  • AbstractExecutorService 一些基本實現,會發現submit()本質上還是調用的是execute()方法,有點像模板方法
  • ThreadPoolExecutor 大量具體的實現,包括Executor#execute
  • ScheduledThreadPoolExecutor 定時調度線程池的實現

Executors

Executors是Executor的工廠,分以下幾類方法:

  • 創建和返回ExecutorService
  • 創建和返回ScheduledExecutorService
  • 創建和返回不可修改配置的線程池,這種線程池一旦創建完成後,後續的其他代碼是不能修改線程池參數的。
  • 創建和返回ThreadFactory
  • 創建和返回Callable,將Runnable適配爲Callable

Executors提供了常用策略的線程池創建,當然如果你也可以直接使用ThreadPoolExecutorScheduledThreadPoolExecutor 創建你想要的線程池。

  • newFixedThreadPool
    創建一個固定長度的線程池

         /*
         * @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>());
        }
    

    另一個對應的工程方法需要提供ThreadFactory,用於線程池創建線程

  • newCachedThreadPool
    緩存線程池

        /*
         * @return the newly created thread pool
         */
        public static ExecutorService newCachedThreadPool() {
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                          60L, TimeUnit.SECONDS,
                                          new SynchronousQueue<Runnable>());
        }
    

    另一個對應的工程方法需要提供ThreadFactory,用於線程池創建線程

  • newSingleThreadExecutor
    單線程的Executor

         /*
         * @return the newly created single-threaded Executor
         */
        public static ExecutorService newSingleThreadExecutor() {
            return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1,
                                        0L, TimeUnit.MILLISECONDS,
                                        new LinkedBlockingQueue<Runnable>()));
        }
    

    另一個對應的工程方法需要提供ThreadFactory,用於線程池創建線程

  • newScheduledThreadPool
    定時調度的線程池

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

    另一個對應的工程方法需要提供ThreadFactory,用於線程池創建線程

  • newSingleThreadScheduledExecutor

    單線程定時調度線程池

     public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
            return new DelegatedScheduledExecutorService
                (new ScheduledThreadPoolExecutor(1));
        }
    

    另一個對應的工程方法需要提供ThreadFactory,用於線程池創建線程

歸根結底還是配置ThreadPoolExecutorScheduledThreadPoolExecutor,所以上面不詳細解釋每個工廠方法的特性,只要明白了ThreadPoolExecutorScheduledThreadPoolExecutor 配置一切都會明白。

配置ThreadPoolExecutor

ThreadPoolExecutor通用構造函數

    /**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        ...
    }                          
  • 線程的創建與銷燬
    corePoolSize沒有任務執行時大的線程池的大小,並且只有工作隊列workQueue滿了纔會創建超出這個數量的線程,但不會超過maximumPoolSize

    在創建ThreadPoolExecutor初期,線程並不會立即啓動,而是等待任務提交時纔會啓動,除非調用prestartAllCoreThreads

    maximumPoolSize表示可同時活動的線程數量上限。
    當某個線程的空閒時間(沒有任務運行)超過存活時間keepAliveTime,那麼這個線程將被標記爲可回收,並且線程池的當前大小超過了corePoolSize 時,這個線程將被終止
    newFixedThreadPool的工廠方法的corePoolSizemaximumPoolSize一樣大,keepAliveTime爲0,所以線程池會一直保持設置的大小,不擴展也不回收。
    newCachedThreadPool的工廠方法的corePoolSize爲0和maximumPoolSizeInteger.MAX_VALUEkeepAliveTime爲60s,是一個無界線程池,線程空閒會被回收。

    如果把corePoolSize設置爲0(像newCachedThreadPool),工作隊列改爲其他的非SynchronousQueue工作隊列,例如大小爲20LinkedBlockingQueue,那麼只有等工作隊列填滿20任務後,纔開始執行。

  • 管理隊列任務
    BlockingQueue是一個阻塞隊列,阻塞隊列提供了puttake方法,以及支持定時的offerpoll方法。如果隊列已經滿了,那麼put方法將阻塞直到有空間可用;如果隊列爲空,那麼take方法將阻塞直到元素可用。隊列可以是有界的也可以是無界的,無界隊列永遠都不會充滿,因此無解隊列的put永遠不會阻塞。基本的任務排隊方法有:無解隊列、有界隊列、和同步移交(Synchronous Handoff)。
    newFixedThreadPoolnewSingleThreadExecutor使用無界的LinkedBlockingQueue,默認構造的大小是Integer.MAX_VALUE

        /**
         * Creates a {@code LinkedBlockingQueue} with a capacity of
         * {@link Integer#MAX_VALUE}.
         */
        public LinkedBlockingQueue() {
            this(Integer.MAX_VALUE);
        }
    

    更穩妥的策略是使用有界隊列避免資源耗盡的情況發生,例如使用ArrayBlockingQueue、有界的LinkedBlockingQueue(構造傳值)、PriorityBlockingQueue(這是一個優先隊列,構造傳入Comparator)。但是隊列滿了怎麼辦?這時的飽和策略由RejectedExecutionHandler實現決定。
    對於非常大或者無界的線程池,可以使用SynchronousQueue來避免排隊(同步移交Synchronous Handoff),直接將任務從生產者移交給工作線程。SynchronousQueue不是一個真正的隊列,而是線程之間移交的機制。要將元素放入SynchronousQueue中,必須有一個線程正在等待接受這個元素。如果沒有線程等待,並且線程池的當前大小小於maximumPoolSize,那麼ThreadPoolExecutor會創建一個新線程。當超過maximumPoolSize時,根據飽和策略被拒絕。只有線程池是無界的或任務可以被拒絕時,SynchronousQueue纔有價值。例如,newCachedThreadPoolmaximumPoolSizeInteger.MAX_VALUE

  • 飽和策略
    JDK提供了幾種不同的RejectedExecutionHandler實現,每種都包含不同的飽和策略:AbortPolicyCallerRunsPolicyDiscardPolicyDiscardOldestPolicy
    AbortPolicy是默認的飽和策略,該策略會拋出未檢查異常RejectedExecutionException
    DiscardPolicy當新提交的任務無法保存到隊列中等待執行時,會拋棄該任務。
    DiscardOldestPolicy 會拋棄下一個被執行的任務,然後重新提交新任務。不能和PriorityBlockingQueue使用,因爲會拋棄優先級最高的任務。
    CallerRunsPolicy 該策略實現一直調節機制,不會拋棄任務,也不會拋出異常,而是將某些任務回退到調用者,從而降低任務流量。當線程池滿,工作隊列滿,它會把新任務交給調用者(執行execute任務提交的線程)的線程執行。任務提交的線程也變成了一個執行任務的線程,可以緩解任務提交量。

  • 線程工廠
    你可以實現ThreadFactory,自定義一個線程工廠,指定一個有意義的線程名,或者給線程設置UncaughtExceptionHandler等等。

public interface ThreadFactory {

    /**
     * Constructs a new {@code Thread}.  Implementations may also initialize
     * priority, name, daemon status, {@code ThreadGroup}, etc.
     *
     * @param r a runnable to be executed by new thread instance
     * @return constructed thread, or {@code null} if the request to
     *         create a thread is rejected
     */
    Thread newThread(Runnable r);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章