大話線程池

why線程池

上一篇中有介紹了線程與進程的區別【線程與進程】;JDK中已經提供了Thread類和runnable接口來創建和啓動一個線程了, 爲什麼還需線程池呢? 因爲創建和銷燬線程是需要有成本的,如果頻繁的去做這個事情,那麼會很浪費CPU的資源,進而影響之下的效率;極端情況下也可能引起OOM,導致系統掛掉。我們需要一個管理線程的池子來管理我們的任務,提高CPU的使用率同時又能夠解決資源浪費的弊端。

ThreadPoolExecutor

在著名的concurrent包下有一個ThreadPoolExecutor類,可以看下里面的代碼邏輯。我們看下這個類的構造方法:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }

參數說明:
1.corePoolSize:*corePoolSize the number of threads to keep in the pool, even if they are idle, unless {@code allowCoreThreadTimeOut} is set; 就是說核心線程數在一直保留在池子中,即使沒有任務可執行; 如果設置了allowCoreThreadTimeOut,那麼核心線程會超時一定時間滯後銷燬掉;*
2.maximumPoolSize: the maximum number of threads to allow in the pool; 線程池中最大的線程數目
3.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; 這是針對超過核心線程數的線程任務來說的, 如果超過一定時間,那麼就會被回收。
4.BlockingQueue workQueue: 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 這個參數設置任務隊列的類型. 常見的類型:ArrayBlockingQueue,LinkedBlockingQueue,SynchronousQueue

5.RejectedExecutionHandler:handler the handler to use when execution is blocked because the thread bounds and queue capacities are reached;拒絕策略,當任務數到最大的隊列大小後,定義任務處理的策略;總共有5種拒絕策略。
AbortPolicy:默認策略,不執行任務,直接拋出異常。
DiscardPolicy:直接拋棄,不執行
DiscardOldestPolicy:拋棄最老的任務,再執行
CallerRunsPolicy:由調用者本身去啓線程執行。

線程池工作流程

這裏寫圖片描述

具體參見上圖:
a.任務數量是否達到核心線程大小:如果沒有,則執行任務,
b.任務數量達到了核心,則將任務push入隊列等待執行。
c.隊列已經滿了,則新建線程執行,直到最大線程數
d.如果任務隊列滿了, 並且達到最大線程數,則執行拒絕策略。

常見線程池

如果我們不想自己去配置線程池參數,JDK幫我們定義了幾個比較有特色的池子,我們可以直接拿來用。

CachedThreadPool: These pools will typically improve the performance of programs that execute many short-lived asynchronous tasks.
優勢:短生命週期的異步任務時,有優勢,可以提高程序的性能;可以看到是用的SynchronousQueue。可緩存的池子,如果不存在則創建一個新的池子,如果60秒之後未有任務,則超時移除。

NewFixedThreadPool:這個會重複使用固定數量的線程 , 採用的是無邊界隊列,但是需要注意內存溢出的問題。
如果線程在執行過程中因爲錯誤而中止,新線程會替代它的位置來執行後續的任務。

SingleThreadPool:只會使用單個工作線程來執行一個無邊界的隊列。如果線程遇到錯誤中止,它是無法使用替代線程的。



    /**
     * 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>());
    }  
    /**
     * 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}.
     */
   public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
        /**
     * 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>()));
    }

開發實踐tips

1.對於任務負載比較高的應用,不合適用無界隊列,會存在內存溢出的風險以及高的延遲; 所以最好將隊列根據實際情況設置成有邊界的隊列。
2.根據場景使用合適的拒絕策略;以如果選用該策略,請注意異常的獲取在execute任務的時候。
3.負載比較低的應用,可以直接用newFixTheadPool,大小可根據cpu核數確定。

發佈了32 篇原創文章 · 獲贊 26 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章