newFixedThreadPool

public static ExecutorService newCachedThreadPool()創建一個可根據需要創建新線程的線程池,但是在以前構造的線程可用時將重用它們。對於執行很多短期異步任務的程序而言,這些線程池通常可提高程序性能。調用 execute 將重用以前構造的線程(如果線程可用)。如果現有線程沒有可用的,則創建一個新線程並添加到池中。終止並從緩存中移除那些已有 60 秒鐘未被使用的線程。因此,長時間保持空閒的線程池不會使用任何資源。注意,可以使用 ThreadPoolExecutor 構造方法創建具有類似屬性但細節不同(例如超時參數)的線程池。 

public static ExecutorService newFixedThreadPool(int nThreads)創建一個可重用固定線程數的線程池,以共享的無界隊列方式來運行這些線程。在任意點,在大多數 nThreads 線程會處於處理任務的活動狀態。如果在所有線程處於活動狀態時提交附加任務,則在有可用線程之前,附加任務將在隊列中等待。如果在關閉前的執行期間由於失敗而導致任何線程終止,那麼一個新線程將代替它執行後續的任務(如果需要)。在某個線程被顯式地關閉之前,池中的線程將一直存在。 

新的線程加入後,如果正在運行的線程達到了上限,則會阻塞,直到有了空閒的線程來運行。 public static ExecutorService newCachedThreadPool()創建一個可根據需要創建新線程的線程池,但是在以前構造的線程可用時將重用它們。對於執行很多短期異步任務的程序而言,這些線程池通常可提高程序性能。調用 execute 將重用以前構造的線程(如果線程可用)。如果現有線程沒有可用的,則創建一個新線程並添加到池中。終止並從緩存中移除那些已有 60 秒鐘未被使用的線程。因此,長時間保持空閒的線程池不會使用任何資源。注意,可以使用 ThreadPoolExecutor 構造方法創建具有類似屬性但細節不同(例如超時參數)的線程池。 

public static ExecutorService newFixedThreadPool(int nThreads)創建一個可重用固定線程數的線程池,以共享的無界隊列方式來運行這些線程。在任意點,在大多數 nThreads 線程會處於處理任務的活動狀態。如果在所有線程處於活動狀態時提交附加任務,則在有可用線程之前,附加任務將在隊列中等待。如果在關閉前的執行期間由於失敗而導致任何線程終止,那麼一個新線程將代替它執行後續的任務(如果需要)。在某個線程被顯式地關閉之前,池中的線程將一直存在。 

新的線程加入後,如果正在運行的線程達到了上限,則會阻塞,直到有了空閒的線程來運行。 

public static ExecutorService newCachedThreadPool()創建一個可根據需要創建新線程的線程池,但是在以前構造的線程可用時將重用它們。對於執行很多短期異步任務的程序而言,這些線程池通常可提高程序性能。調用 execute 將重用以前構造的線程(如果線程可用)。如果現有線程沒有可用的,則創建一個新線程並添加到池中。終止並從緩存中移除那些已有 60 秒鐘未被使用的線程。因此,長時間保持空閒的線程池不會使用任何資源。注意,可以使用 ThreadPoolExecutor 構造方法創建具有類似屬性但細節不同(例如超時參數)的線程池。 

public static ExecutorService newFixedThreadPool(int nThreads)創建一個可重用固定線程數的線程池,以共享的無界隊列方式來運行這些線程。在任意點,在大多數 nThreads 線程會處於處理任務的活動狀態。如果在所有線程處於活動狀態時提交附加任務,則在有可用線程之前,附加任務將在隊列中等待。如果在關閉前的執行期間由於失敗而導致任何線程終止,那麼一個新線程將代替它執行後續的任務(如果需要)。在某個線程被顯式地關閉之前,池中的線程將一直存在。 

新的線程加入後,如果正在運行的線程達到了上限,則會阻塞,直到有了空閒的線程來運行。 


public static ExecutorService newCachedThreadPool()創建一個可根據需要創建新線程的線程池,但是在以前構造的線程可用時將重用它們。對於執行很多短期異步任務的程序而言,這些線程池通常可提高程序性能。調用 execute 將重用以前構造的線程(如果線程可用)。如果現有線程沒有可用的,則創建一個新線程並添加到池中。終止並從緩存中移除那些已有 60 秒鐘未被使用的線程。因此,長時間保持空閒的線程池不會使用任何資源。注意,可以使用 ThreadPoolExecutor 構造方法創建具有類似屬性但細節不同(例如超時參數)的線程池。 

public static ExecutorService newFixedThreadPool(int nThreads)創建一個可重用固定線程數的線程池,以共享的無界隊列方式來運行這些線程。在任意點,在大多數 nThreads 線程會處於處理任務的活動狀態。如果在所有線程處於活動狀態時提交附加任務,則在有可用線程之前,附加任務將在隊列中等待。如果在關閉前的執行期間由於失敗而導致任何線程終止,那麼一個新線程將代替它執行後續的任務(如果需要)。在某個線程被顯式地關閉之前,池中的線程將一直存在。 

新的線程加入後,如果正在運行的線程達到了上限,則會阻塞,直到有了空閒的線程來運行。 
創建newFixedThreadPool的方法:

[java] view plain copy
  1. public static ExecutorService newFixedThreadPool(int nThreads) {  
  2.     return new ThreadPoolExecutor(nThreads, nThreads,  
  3.                                   0L, TimeUnit.MILLISECONDS,  
  4.                                   new LinkedBlockingQueue<Runnable>());  
  5. }  
[java] view plain copy
  1. public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {  
  2.     return new ThreadPoolExecutor(nThreads, nThreads,  
  3.                                   0L, TimeUnit.MILLISECONDS,  
  4.                                   new LinkedBlockingQueue<Runnable>(),  
  5.                                   threadFactory);  
  6. }  
上面這兩個方法是創建固定數量的線程池的兩種方法,兩者的區別是:第二種創建方法多了一個線程工廠的方法。我們繼續看ThreadPoolExecutor這個類中的構造函數:

ThreadPoolExecutor的構造函數:

[java] view plain copy
  1. public ThreadPoolExecutor(int corePoolSize,  
  2.                           int maximumPoolSize,  
  3.                           long keepAliveTime,  
  4.                           TimeUnit unit,  
  5.                           BlockingQueue<Runnable> workQueue) {  
  6.     this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,  
  7.          Executors.defaultThreadFactory(), defaultHandler);  
  8. }  
[java] view plain copy
  1. public ThreadPoolExecutor(int corePoolSize,  
  2.                           int maximumPoolSize,  
  3.                           long keepAliveTime,  
  4.                           TimeUnit unit,  
  5.                           BlockingQueue<Runnable> workQueue,  
  6.                           ThreadFactory threadFactory,  
  7.                           RejectedExecutionHandler handler) {  
  8.     if (corePoolSize < 0 ||  
  9.         maximumPoolSize <= 0 ||  
  10.         maximumPoolSize < corePoolSize ||  
  11.         keepAliveTime < 0)  
  12.         throw new IllegalArgumentException();  
  13.     if (workQueue == null || threadFactory == null || handler == null)  
  14.         throw new NullPointerException();  
  15.     this.corePoolSize = corePoolSize;  
  16.     this.maximumPoolSize = maximumPoolSize;  
  17.     this.workQueue = workQueue;  
  18.     this.keepAliveTime = unit.toNanos(keepAliveTime);  
  19.     this.threadFactory = threadFactory;  
  20.     this.handler = handler;  
  21. }  
ThreadPollExecutor中的所有的構造函數最終都會調用上面這個構造函數,接下來我們來分析一下這些參數的含義:

corePoolSize:

線程池啓動後,在池中保持的線程的最小數量。需要說明的是線程數量是逐步到達corePoolSize值的。例如corePoolSize被設置爲10,而任務數量只有5,則線程池中最多會啓動5個線程,而不是一次性地啓動10個線程。

maxinumPoolSize:

線程池中能容納的最大線程數量,如果超出,則使用RejectedExecutionHandler拒絕策略處理。

keepAliveTime:

線程的最大生命週期。這裏的生命週期有兩個約束條件:一:該參數針對的是超過corePoolSize數量的線程;二:處於非運行狀態的線程。舉個例子:如果corePoolSize(最小線程數)爲10,maxinumPoolSize(最大線程數)爲20,而此時線程池中有15個線程在運行,過了一段時間後,其中有3個線程處於等待狀態的時間超過keepAliveTime指定的時間,則結束這3個線程,此時線程池中則還有12個線程正在運行。

unit:

這是keepAliveTime的時間單位,可以是納秒,毫秒,秒,分鐘等。

workQueue:

任務隊列。當線程池中的線程都處於運行狀態,而此時任務數量繼續增加,則需要一個容器來容納這些任務,這就是任務隊列。這個任務隊列是一個阻塞式的單端隊列。

threadFactory:

定義如何啓動一個線程,可以設置線程的名稱,並且可以確定是否是後臺線程等。

handler:

拒絕任務處理器。由於超出線程數量和隊列容量而對繼續增加的任務進行處理的程序。
OK,ThreadPoolExecutor中的主要參數介紹完了。我們再說一下線程的管理過程:首先創建一個線程池,然後根據任務的數量逐步將線程增大到corePoolSize,如果此時仍有任務增加,則放置到workQueue中,直到workQueue爆滿爲止,然後繼續增加池中的線程數量(增強處理能力),最終達到maxinumPoolSize。那如果此時還有任務要增加進來呢?這就需要handler來處理了,或者丟棄新任務,或者拒絕新任務,或者擠佔已有的任務。在任務隊列和線程池都飽和的情況下,一旦有線程處於等待(任務處理完畢,沒有新任務)狀態的時間超過keepAliveTime,則該線程終止,也就是說池中的線程數量會逐漸降低,直至爲corePoolSize數量爲止。在《編寫高質量代碼 改善Java程序的151個建議》這本書裏舉的這個例子很形象:


OK,接下來我們來看一下怎麼往任務隊裏中放入線程任務:在java.util.concurrent.AbstractExecutorService這個類的submit方法

submit方法

[java] view plain copy
  1. public Future<?> submit(Runnable task) {  
  2.     if (task == nullthrow new NullPointerException();  
  3.     RunnableFuture<Void> ftask = newTaskFor(task, null);  
  4.     execute(ftask);//執行任務  
  5.     return ftask;  
  6. }  
  7.   
  8. /** 
  9.  * @throws RejectedExecutionException {@inheritDoc} 
  10.  * @throws NullPointerException       {@inheritDoc} 
  11.  */  
  12. public <T> Future<T> submit(Runnable task, T result) {  
  13.     if (task == nullthrow new NullPointerException();  
  14.     RunnableFuture<T> ftask = newTaskFor(task, result);  
  15.     execute(ftask);//執行任務  
  16.     return ftask;  
  17. }  
  18.   
  19. /** 
  20.  * @throws RejectedExecutionException {@inheritDoc} 
  21.  * @throws NullPointerException       {@inheritDoc} 
  22.  */  
  23. public <T> Future<T> submit(Callable<T> task) {  
  24.     if (task == nullthrow new NullPointerException();  
  25.     RunnableFuture<T> ftask = newTaskFor(task);  
  26.     execute(ftask);//執行任務  
  27.     return ftask;  
  28. }  
這是三個重載方法,分別對應Runnable、帶結果的Runnable接口和Callable回調函數。其中的newTaskFor也是一個重載的方法,它通過層層的包裝,把Runnable接口包裝成了適配RunnableFuture的實現類,底層實現如下:

[java] view plain copy
  1. public FutureTask(Runnable runnable, V result) {  
  2.     this.callable = Executors.callable(runnable, result);  
  3.     this.state = NEW;       // ensure visibility of callable  
  4. }  
[java] view plain copy
  1. public static <T> Callable<T> callable(Runnable task, T result) {  
  2.     if (task == null)  
  3.         throw new NullPointerException();  
  4.     return new RunnableAdapter<T>(task, result);  
  5. }  
[java] view plain copy
  1. static final class RunnableAdapter<T> implements Callable<T> {  
  2.     final Runnable task;  
  3.     final T result;  
  4.     RunnableAdapter(Runnable task, T result) {  
  5.         this.task = task;  
  6.         this.result = result;  
  7.     }  
  8.     public T call() {  
  9.         task.run();  
  10.         return result;  
  11.     }  
  12. }  
在submit中最重要的是execute這個方法,這個方法也是我們分析的重點

execute方法:

[java] view plain copy
  1. public void execute(Runnable command) {  
  2.     if (command == null)  
  3.         throw new NullPointerException();  
  4.     int c = ctl.get();  
  5.     if (workerCountOf(c) < corePoolSize) {//  
  6.         if (addWorker(command, true))  
  7.             return;  
  8.         c = ctl.get();  
  9.     }  
  10.     if (isRunning(c) && workQueue.offer(command)) {  
  11.         int recheck = ctl.get();  
  12.         if (! isRunning(recheck) && remove(command))  
  13.             reject(command);  
  14.         else if (workerCountOf(recheck) == 0)  
  15.             addWorker(nullfalse);  
  16.     }  
  17.     else if (!addWorker(command, false))  
  18.         reject(command);  
  19. }  
在這個方法中分爲三部分
1、如果少於corePoolSize數量的線程在運行,則啓動一個新的線程並把傳進來的Runnable做爲第一個任務。然後會檢查線程的運行狀態和worker的數量,阻止不符合要求的任務添加到線程中
2、如果一個任務成功的放入到了隊列中,我們仍然需要二次檢查我們是否應該添加線程或者停止。因此我們重新檢查線程狀態,是否需要回滾隊列,或者是停止或者是啓動一個新的線程
3、如果我們不能添加隊列任務了,但是仍然在往隊列中添加任務,如果添加失敗的話,用拒絕策略來處理。
這裏最主要的是addWorker這個方法:
[java] view plain copy
  1. try {  
  2.     w = new Worker(firstTask);  
  3.     final Thread t = w.thread;  
  4.     if (t != null) {  
  5.         final ReentrantLock mainLock = this.mainLock;  
  6.         mainLock.lock();  
  7.         try {  
  8.             // Recheck while holding lock.  
  9.             // Back out on ThreadFactory failure or if  
  10.             // shut down before lock acquired.  
  11.             int rs = runStateOf(ctl.get());  
  12.   
  13.             if (rs < SHUTDOWN ||  
  14.                 (rs == SHUTDOWN && firstTask == null)) {  
  15.                 if (t.isAlive()) // precheck that t is startable  
  16.                     throw new IllegalThreadStateException();  
  17.                 workers.add(w);  
  18.                 int s = workers.size();  
  19.                 if (s > largestPoolSize)  
  20.                     largestPoolSize = s;  
  21.                 workerAdded = true;  
  22.             }  
  23.         } finally {  
  24.             mainLock.unlock();  
  25.         }  
  26.         if (workerAdded) {  
  27.             t.start();  
  28.             workerStarted = true;  
  29.         }  
  30.     }  
  31. finally {  
  32.     if (! workerStarted)  
  33.         addWorkerFailed(w);  
  34. }  
我們在這個方法裏創建一個線程,注意這個線程不是我們的任務線程,而是經過包裝的Worker線程。所以這裏的run方法是Worker這個類中的run方法。execute方法是通過Worker類啓動的一個工作線程,執行的是我們的第一個任務,然後該線程通過getTask方法從任務隊列總獲取任務,之後再繼續執行。這個任務隊列是一個BlockingQueue,是一個阻塞式的,也就是說如果該隊列元素爲0,則保持等待狀態。直到有任務進入爲止。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章