JDK中的線程池 :工作隊列和拒絕策略 上

JDK 可以創建使用Executors 創建5種類型的線程池

 Executors.newCachedThreadPool();

該方法返回一個可根據實際情況調整線程數量的線程池。線程池的線程數量不確定,但若有空閒線程可以複用,則會優先 使用可複用的線程。 若所有線程均工作,又有新的任務提交,則會創建新的線程處理任務。所有線程在當前安任務執行完畢後,將返回線程池進行復用。

  Executors.newFixedThreadPool(nThreads)

該方法創建一個固定數量的線程池。該線程池中的線程數量始終不變。當有一個新的任務提交時,線程池中若有空閒線程,則立即執行,若沒有,則新的任務會被 暫存在一個任務隊列中,待有空閒線程時,便處理在任務隊列中的任務。

  Executors.newSingleThreadExecutor()

該方法返回一個只有一個線程的線程池。若多餘一個任務被提交到該線程池,任務會被保存哎ige任務隊列中,待線程空閒,按先入先出的順序執行隊列中的任務。

      Executors.newSingleThreadScheduledExecutor()

該方法返回一個ScheduledExecutorService對象,線程池大小爲1。ScheduledExectorService接口在ExecutorService接口之上擴展了在給定時間執行某任務的功能, 如在固定的延時之後執行或者週期性執行某個任務。

  Executors.newScheduledThreadPool(  corePoolSize)

該方法可以返回固定數量的線程池。

具體代碼實現如下:


public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

我們看下這個構造函數的源碼及解釋:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)  

corePoolSize 指定了線程的數量。
maximumPoolSize指定了線程池的最大數量
keepAliveTime 線程池中線程的數量超過了corePoolSize 的空閒線程存活的時間。
unit keepAliveTime的單位
workQueue 任務隊列
threadFactory 線程工廠
handler 拒絕策略,來不及處理任務時,採用怎樣的方式拒絕任務。

下面介紹下 workQueue 和handler

參數workQueue 指被提交但未執行的任務隊列,它是一個BlockingQueue接口的對象,僅用於存放Runnable對象。 根據隊列功能分類,在ThreadPoolExecutor的構造函數中可使用一下幾種BlockingQueue。

直接提交的隊列: 該功能由 SynchronousQueue對象提供。SynchronousQueue 是一個特殊的BlocingQueue。 它沒有容量,每一個插入操作都要等待一個相應的刪除操作,反之,每一個刪除操作都要等待對應的插入操作。如皋市使用SynchronousQueue,提交的任務不會被真實的保存,而總是將新任務提交給線程執行,如果沒有空閒的進程,則嘗試創建新的進程,如果進程的數量已達到最大值,則執行拒絕策略。

有界的任務隊列
有界的任務隊列可以使用ArrayBlockingQueue實現。當使用有界隊列時,若有新的任務需要執行,如果線程池的實際線程數小於corePoolSize,則會優先創建新的線程,若大於corePoolSize,則會將新任務假如等待隊列。若等待隊列已滿,無法加入,在總線程數,不大於maximumPoolSize的前提下,創建新的進程執行任務。若大於maximumPoolSize,則執行拒絕策略。

無界的任務隊列
無界的任務隊列可以通過LinkedBlockingQueue類實現。與有界隊列相反,除非系統資源耗盡,否則無界的任務隊列不存在任務入隊失敗的情況。當有新的任務到來,系統的線程數小於corePoolSize時,線程池會產生新的線程執行任務,但當系統的線程數達到corePoolSize後,就會繼續增加。若後續仍有新的任務假如,而又沒有空閒的線程資源,則任務直接進入對列等待。若任務創建和處理的速度差異很大,無界隊列會保持快速增長,知道耗盡系統內存。

任務優先隊列
優先任務隊列是帶有執行優先級的隊列,它通過PriorityBlockingQueue實現,可以控制任務的只想你個先後順序。它是一個特殊的無界隊列。

ThreadPoolExecutor 核心執行代碼如下:


public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         * 
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

這個方法會有三步:
1 如果當前線程的數量小於corePoolSize,則執行addWorker 分配線程執行任務。
2 如果當前的線程數量大於corePoolSize,則將線程放進workQueue隊列中,等待執行。
如果放入失敗,則將任務直接提交,分配線程進行執行。
3 如果當前線程的數量已經達到了maximumPoolSize 則執行拒絕策略。

這裏寫圖片描述

拒絕策略(下篇繼續)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章