Java的Executor框架和線程池實現原理

一,Java的Executor框架


1,Executor接口

[java] view plain copy
  1. public interface Executor {  
  2.      void execute(Runnable command);  
  3.  }  
Executor接口是Executor框架中最基礎的部分,定義了一個用於執行Runnable的execute方法,它沒有實現類只有另一個重要的子接口ExecutorService

2,ExecutorService接口

[java] view plain copy
  1. //繼承自Executor接口  
  2.   public interface ExecutorService extends Executor {  
  3.       /** 
  4.        * 關閉方法,調用後執行之前提交的任務,不再接受新的任務 
  5.        */  
  6.       void shutdown();  
  7.       /** 
  8.        * 從語義上可以看出是立即停止的意思,將暫停所有等待處理的任務並返回這些任務的列表 
  9.        */  
  10.      List<Runnable> shutdownNow();  
  11.      /** 
  12.       * 判斷執行器是否已經關閉 
  13.       */  
  14.      boolean isShutdown();  
  15.      /** 
  16.       * 關閉後所有任務是否都已完成 
  17.       */  
  18.      boolean isTerminated();  
  19.      /** 
  20.       * 中斷 
  21.       */  
  22.     boolean awaitTermination(long timeout, TimeUnit unit)  
  23.          throws InterruptedException;  
  24.      /** 
  25.       * 提交一個Callable任務 
  26.       */  
  27.      <T> Future<T> submit(Callable<T> task);  
  28.      /** 
  29.       * 提交一個Runable任務,result要返回的結果 
  30.       */  
  31.      <T> Future<T> submit(Runnable task, T result);  
  32.      /** 
  33.       * 提交一個任務 
  34.       */  
  35.      Future<?> submit(Runnable task);  
  36.      /** 
  37.       * 執行所有給定的任務,當所有任務完成,返回保持任務狀態和結果的Future列表 
  38.       */  
  39.      <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)  
  40.          throws InterruptedException;  
  41.      /** 
  42.       * 執行給定的任務,當所有任務完成或超時期滿時(無論哪個首先發生),返回保持任務狀態和結果的 Future 列表。 
  43.       */  
  44.      <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,  
  45.                                    long timeout, TimeUnit unit)  
  46.          throws InterruptedException;  
  47.      /** 
  48.       * 執行給定的任務,如果某個任務已成功完成(也就是未拋出異常),則返回其結果。 
  49.       */  
  50.      <T> T invokeAny(Collection<? extends Callable<T>> tasks)  
  51.          throws InterruptedException, ExecutionException;  
  52.      /** 
  53.       * 執行給定的任務,如果在給定的超時期滿前某個任務已成功完成(也就是未拋出異常),則返回其結果。 
  54.       */  
  55.      <T> T invokeAny(Collection<? extends Callable<T>> tasks,  
  56.                      long timeout, TimeUnit unit)  
  57.          throws InterruptedException, ExecutionException, TimeoutException;  
  58.  }  
ExecutorService接口繼承自Executor接口,定義了終止、提交,執行任務、跟蹤任務返回結果等方法

1,execute(Runnable command):履行Ruannable類型的任務,

2,submit(task):可用來提交Callable或Runnable任務,並返回代表此任務的Future對象
3,shutdown():在完成已提交的任務後封閉辦事,不再接管新任務,

4,shutdownNow():停止所有正在履行的任務並封閉辦事。
5,isTerminated():測試是否所有任務都履行完畢了。,

6,isShutdown():測試是否該ExecutorService已被關閉

3,Executors的靜態方法:負責生成各種類型的ExecutorService線程池實例

+newFixedThreadPool(numberOfThreads:int):(固定線程池)ExecutorService 創建一個固定線程數量的線程池,並行執行的線程數量不變,線程當前任務完成後,可以被重用執行另一個任務
+newCachedThreadPool():(可緩存線程池)ExecutorService 創建一個線程池,按需創建新線程,就是有任務時才創建,空閒線程保存60s,當前面創建的線程可用時,則重用它們

+new SingleThreadExecutor();(單線程執行器線程池中只有一個線程,依次執行任務


+new ScheduledThreadPool():線程池按時間計劃來執行任務,允許用戶設定執行任務的時間

+new SingleThreadScheduledExcutor();線程池中只有一個線程,它按規定時間來執行任務

4,Runnable、Callable、Future接口

Runnable接口:

[java] view plain copy
  1. // 實現Runnable接口的類將被Thread執行,表示一個基本的任務  
  2.   public interface Runnable {  
  3.       // run方法就是它所有的內容,就是實際執行的任務  
  4.       public abstract void run();  
  5.   }  
Callable接口:與Runnable接口的區別在於它接收泛型,同時它執行任務後帶有返回內容

[java] view plain copy
  1. // Callable同樣是任務,與Runnable接口的區別在於它接收泛型,同時它執行任務後帶有返回內容  
  2.   public interface Callable<V> {  
  3.       // 相對於run方法的帶有返回值的call方法  
  4.       V call() throws Exception;  
  5. }  

Runnable接口和Callable接口的實現類,都可以被ThreadPoolExecutor和ScheduledThreadPoolExecutor執行,他們之間的區別是Runnable不會返回結果,而Callable可以返回結果。

Executors可以把一個Runnable對象轉換成Callable對象:

[java] view plain copy
  1. public static Callable<Object> callable(Runnbale task);  
Executors把一個Runnable和一個待返回的結果包裝成一個Callable的API:
[java] view plain copy
  1. public static<T> Callable<T> callable(Runnbale task,T result);  
當把一個Callable對象(Callable1,Callable2)提交給ThreadPoolExecutor和ScheduledThreadPoolExecutor執行時,submit(...)會向我們返回一個FutureTask對象。我們執行FutureTask.get()來等待任務執行完成,當任務完成後,FutureTask.get()將返回任務的結果。


Future接口:

[java] view plain copy
  1. // Future代表異步任務的執行結果  
  2.   public interface Future<V> {  
  3.     
  4.       /** 
  5.        * 嘗試取消一個任務,如果這個任務不能被取消(通常是因爲已經執行完了),返回false,否則返回true。 
  6.        */  
  7.       boolean cancel(boolean mayInterruptIfRunning);  
  8.     
  9.       /** 
  10.       * 返回代表的任務是否在完成之前被取消了 
  11.       */  
  12.      boolean isCancelled();  
  13.    
  14.      /** 
  15.       * 如果任務已經完成,返回true 
  16.       */  
  17.     boolean isDone();  
  18.    
  19.      /** 
  20.       * 獲取異步任務的執行結果(如果任務沒執行完將等待) 
  21.       */  
  22.     V get() throws InterruptedException, ExecutionException;  
  23.    
  24.      /** 
  25.       * 獲取異步任務的執行結果(有最常等待時間的限制) 
  26.       * 
  27.       *  timeout表示等待的時間,unit是它時間單位 
  28.       */  
  29.      V get(long timeout, TimeUnit unit)  
  30.          throws InterruptedException, ExecutionException, TimeoutException;  
  31.  }  
Future就是對於具體的Runnable或者Callable任務的執行結果進行取消查詢是否完成獲取結果。必要時可以通過get方法獲取執行結果,該方法會阻塞直到任務返回結果


在Future接口中聲明瞭5個方法,下面依次解釋每個方法的作用:
+cancel方法用來取消任務,如果取消任務成功則返回true,如果取消任務失敗則返回false。參數mayInterruptIfRunning表示是否允許取消正在執行卻沒有執行完畢的任務,如果設置true,則表示可以取消正在執行過程中的任務。如果任務已經完成,則無論mayInterruptIfRunning爲true還是false,此方法肯定返回false,即如果取消已經完成的任務會返回false;如果任務正在執行,若mayInterruptIfRunning設置爲true,則返回true,若mayInterruptIfRunning設置爲false,則返回false;如果任務還沒有執行,則無論mayInterruptIfRunning爲true還是false,肯定返回true。
+isCancelled方法表示任務是否被取消成功,如果在任務正常完成前被取消成功,則返回 true。
+isDone方法表示任務是否已經完成,若任務完成,則返回true;
+get()方法用來獲取執行結果,這個方法會產生阻塞,會一直等到任務執行完畢才返回;
+get(long timeout, TimeUnit unit)用來獲取執行結果,如果在指定時間內,還沒獲取到結果,就直接返回null。

也就是說Future提供了三種功能:
  1)判斷任務是否完成;
  2)能夠中斷任務;
  3)能夠獲取任務執行結果。


FutureTask:

通常使用FutureTask來處理我們的任務。FutureTask類同時又實現了Runnable接口,所以可以直接提交給Executor執行

[java] view plain copy
  1. FutureTask提供了2個構造器:  
  2.   
  3. public FutureTask(Callable<V> callable) {  
  4. }  
  5. public FutureTask(Runnable runnable, V result) {  
  6. }  
  7.   //事實上,FutureTask是Future接口的一個唯一實現類。  

使用FutureTask實現超時執行的代碼如下:

[java] view plain copy
  1. xecutorService executor = Executors.newSingleThreadExecutor();     
  2. FutureTask<String> future =     
  3.        new FutureTask<String>(new Callable<String>() {//使用Callable接口作爲構造參數     
  4.          public String call() {     
  5.            //真正的任務在這裏執行,這裏的返回值類型爲String,可以爲任意類型     
  6.        }});     
  7. executor.execute(future);     
  8. //在這裏可以做別的任何事情     
  9. try {     
  10.     result = future.get(5000, TimeUnit.MILLISECONDS); //取得結果,同時設置超時執行時間爲5秒。同樣可以用future.get(),不設置執行超時時間取得結果     
  11. catch (InterruptedException e) {     
  12.     futureTask.cancel(true);     
  13. catch (ExecutionException e) {     
  14.     futureTask.cancel(true);     
  15. catch (TimeoutException e) {     
  16.     futureTask.cancel(true);     
  17. finally {     
  18.     executor.shutdown();     
  19. }    


不直接構造Future對象,也可以使用ExecutorService.submit方法來獲得Future對象,submit方法即支持以 Callable接口類型,也支持Runnable接口作爲參數,具有很大的靈活性。使用示例如下:

[java] view plain copy
  1. ExecutorService executor = Executors.newSingleThreadExecutor();     
  2. FutureTask<String> future = executor.submit(     
  3.    new Callable<String>() {//使用Callable接口作爲構造參數     
  4.        public String call() {     
  5.       //真正的任務在這裏執行,這裏的返回值類型爲String,可以爲任意類型     
  6.    }});     
  7. //在這裏可以做別的任何事情     
  8. //同上面取得結果的代碼   


線程池實現原理詳解:

ThreadPoolExecutor是線程池的實現類:

[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)     
(1)corePoolSize(線程池的基本大小):當提交一個任務到線程池時,線程會創建一個線程來執行任務,即使其他空閒的基本線程能創建線程也會創建線程,等到到需要執行的任務數大於線程池基本大小corePoolSize時就不再創建

(2)maximumPoolSize(線程池最大大小):線程池允許最大線程數。如果阻塞隊列滿了,並且已經創建的線程數小於最大線程數,則線程池會再創建新的線程執行。因爲線程池執行任務時是線程池基本大小滿了,後續任務進入阻塞隊列,阻塞隊列滿了,在創建線程。

(3)keepAliveTime(線程活動保持時間):空閒線程的保持存活時間。
(4)TimeUnit(線程活動保持時間的單位):

        TimeUnit.DAYS; //天
        TimeUnit.HOURS; //小時
        TimeUnit.MINUTES; //分鐘
        TimeUnit.SECONDS; //秒
        TimeUnit.MILLISECONDS; //毫秒
        TimeUnit.MICROSECONDS; //微妙
        TimeUnit.NANOSECONDS; //納秒

(5)workQueue(任務隊列):用於保存等待執行的任務的阻塞隊列。一個阻塞隊列,用來存儲等待執行的任務:數組,鏈表,不存元素的阻塞隊列

      5.1)ArrayBlockingQueue;數組結構的有界阻塞隊列,先進先出FIFO
      5.2)LinkedBlockingQueue;鏈表結構的無界阻塞隊列。先進先出FIFO排序元素,靜態方法Executors.newFixedThreadPool使用這個方法

      5.3)SynchronousQueue;不存儲元素的阻塞隊列,就是每次插入操作必須等到另一個線程調用移除操作,靜態方法Executors.newCachedThreadPool使用這個方法

 (6)threadFactory:用於設置創建線程的工廠,可以通過線程工廠給每個創建出來的線程設置更有意義的名字

 (7)handler(飽和策略):表示當拒絕處理任務時的策略。當隊列和線程池都滿了,說明線程池處於飽和狀態,那麼必須採取一種策略處理提交的新任務。這個策略默認情況下是AbortPolicy,表示無法處理新任務時拋出異常。

     ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。

     ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常

     ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)

    ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務

 

我們儘量優先使用Executors提供的靜態方法來創建線程池,如果Executors提供的方法無法滿足要求,再自己通過ThreadPoolExecutor類來創建線程池   

[java] view plain copy
  1. Executors.newFixedThreadPool(int); //創建固定容量大小的緩衝池         
  2. Executors.newCachedThreadPool(); //創建一個緩衝池,緩衝池容量大小爲Integer.MAX_VALUE  
  3. Executors.newSingleThreadExecutor(); //創建容量爲1的緩衝池  

下面是這三個靜態方法的具體實現;

[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. }  
  6. public static ExecutorService newSingleThreadExecutor() {  
  7.     return new FinalizableDelegatedExecutorService  
  8.         (new ThreadPoolExecutor(11,  
  9.                                 0L, TimeUnit.MILLISECONDS,  
  10.                                 new LinkedBlockingQueue<Runnable>()));  
  11. }  
  12. public static ExecutorService newCachedThreadPool() {  
  13.     return new ThreadPoolExecutor(0, Integer.MAX_VALUE,  
  14.                                   60L, TimeUnit.SECONDS,  
  15.                                   new SynchronousQueue<Runnable>());  
  16. }  

從它們的具體實現來看,它們實際上也是調用了ThreadPoolExecutor,只不過參數都已配置好了。

newFixedThreadPool創建的線程池corePoolSize和maximumPoolSize值是相等的(n,n),它使用的LinkedBlockingQueue


newSingleThreadExecutor將corePoolSize和maximumPoolSize都設置爲1(1,1),也使用的LinkedBlockingQueue


newCachedThreadPool將corePoolSize設置爲0,將maximumPoolSize設置爲Integer.MAX_VALUE,使用的SynchronousQueue,也就是說來了任務就創建線程運行,當線程空閒超過60秒,就銷燬線程。

實際中,如果Executors提供的三個靜態方法能滿足要求,就儘量使用它提供的三個方法,因爲自己去手動配置ThreadPoolExecutor的參數有點麻煩,要根據實際任務的類型和數量來進行配置。


1)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. }  
線程池corePoolSize和maximumPoolSize值是相等的(n,n),把keepAliveTime設置0L,意味着多餘的空閒線程會被立即終止。

newFixedThreadPool的execute方法執行過程:

1,如果當前運行線程數少於corePoolSize,則創建新線程來執行任務(優先滿足核心池

2,當前運行線程數等於corePoolSize時,將任務加入LinkedBlockingQueue鏈式阻塞隊列(核心池滿了在進入隊列

3,當線程池的任務完成之後,循環反覆從LinkedBlockingQueue隊列中獲取任務來執行


2)newSingleThreadExecutor:(單線程執行器)

newSingleThreadExecutor是使用單個worker線程的Executors.

[java] view plain copy
  1. public static ExecutorService newSingleThreadExecutor() {  
  2.     return new FinalizableDelegatedExecutorService  
  3.         (new ThreadPoolExecutor(11,  
  4.                                 0L, TimeUnit.MILLISECONDS,  
  5.                                 new LinkedBlockingQueue<Runnable>()));  
  6. }  
newSingleThreadExecutor的execute方法執行過程如下:

  1,當前運行的線程數少於corePoolSize(即當前線程池中午運行的線程),則創建一個新的線程來執行任務

  2,當線程池中有一個運行的線程時,將任務加入阻塞隊列

  3,當線程完成任務時,會無限反覆從鏈式阻塞隊列中獲取任務來執行


3,)newCachedThreadPool:可緩存線程池

[java] view plain copy
  1. public static ExecutorService newCachedThreadPool() {  
  2.     return new ThreadPoolExecutor(0, Integer.MAX_VALUE,  
  3.                                   60L, TimeUnit.SECONDS,  
  4.                                   new SynchronousQueue<Runnable>());  
  5. }  
newCachedThreadPool是一個根據需要創建線程的線程池。

newCachedThreadPool的corePoolSize設置0,即核心池是空,maxmumPoolSize設置爲Integer.MAX_VALUE,即maxmumPool是無界的。keepAliveTime設置60L,當空閒線程等待新任務最長時間是60s,超過60s就終止

三個線程池的特點:

1、newFixedThreadPool創建一個指定工作線程數量的線程池。每當提交一個任務就創建一個工作線程,如果工作線程數量達到線程池初始的最大數corePoolSize,則將提交的任務存入到池隊列中

2、newCachedThreadPool創建一個可緩存的線程池。這種類型的線程池特點是:
1).工作線程的創建數量幾乎沒有限制(其實也有限制的,數目爲Interger. MAX_VALUE), 這樣可靈活的往線程池中添加線程。
2).如果長時間沒有往線程池中提交任務,即如果工作線程空閒了指定的時間(默認爲1分鐘),則該工作線程將自動終止。終止後,如果你又提交了新的任務,則線程池重新創建一個工作線程。

3、newSingleThreadExecutor創建一個單線程化的Executor,即只創建唯一的工作者線程來執行任務,如果這個線程異常結束,會有另一個取代它,保證順序執行(我覺得這點是它的特色)。單工作線程最大的特點是可保證順序地執行各個任務,並且在任意給定的時間不會有多個線程是活動的 


線程池的處理流程:

線程池執行示意圖:


1,首先線程池判斷基本線程池是否已滿(< corePoolSize ?)?沒滿,創建一個工作線程來執行任務。滿了,則進入下個流程。

2,其次線程池判斷工作隊列是否已滿?沒滿,則將新提交的任務存儲在工作隊列裏。滿了,則進入下個流程。

3,最後線程池判斷整個線程池是否已滿(< maximumPoolSize ?)?沒滿,則創建一個新的工作線程來執行任務,滿了,則交給飽和策略來處理這個任務。

總結:線程池優先要創建出基本線程池大小(corePoolSize)的線程數量,沒有達到這個數量時,每次提交新任務都會直接創建一個新線程,當達到了基本線程數量後,又有新任務到達,優先放入等待隊列,如果隊列滿了,纔去創建新的線程(不能超過線程池的最大數maxmumPoolSize)


向線程池提交任務的兩種方式:

1)通過execute()方法

[java] view plain copy
  1. ExecutorService threadpool= Executors.newFixedThreadPool(10);    
  2. threadpool.execute(new Runnable(){...});  
這種方式提交沒有返回值,也就不能判斷任務是否被線程池執行成功

2)通過submit()方法

[java] view plain copy
  1. Future<?> future = threadpool.submit(new Runnable(){...});    
  2.     try {    
  3.             Object res = future.get();//獲取任務執行結果    
  4.         } catch (InterruptedException e) {    
  5.             // 處理中斷異常    
  6.             e.printStackTrace();    
  7.         } catch (ExecutionException e) {    
  8.             // 處理無法執行任務異常    
  9.             e.printStackTrace();    
  10.         }finally{    
  11.             // 關閉線程池    
  12.             executor.shutdown();    
  13.         }    
使用submit 方法來提交任務,它會返回一個Future對象,通過future的get方法來獲取返回值,get方法會阻塞住直到任務完成,而使用get(long timeout, TimeUnit unit)方法則會阻塞一段時間後立即返回,這時有可能任務沒有執行完。


線程池的關閉:

• shutdown():不會立即終止線程池,而是再也不會接受新的任務要等所有任務緩存隊列中的任務都執行完後才終止
• shutdownNow():立即終止線程池,再也不會接受新的任務,並嘗試打斷正在執行的任務,並且清空任務緩存隊列,返回尚未執行的任務

線程池本身的狀態

[java] view plain copy
  1. volatile int runState;     
  2. static final int RUNNING = 0;   //運行狀態  
  3. static final int SHUTDOWN = 1;   //關閉狀態  
  4. static final int STOP = 2;       //停止  
  5. static final int TERMINATED = 3//終止,終結  

1,當創建線程池後,初始時,線程池處於RUNNING狀態
2,如果調用了shutdown()方法,則線程池處於SHUTDOWN狀態,此時線程池不能夠接受新的任務,它會等待所有任務執行完畢,最後終止;
3,如果調用了shutdownNow()方法,則線程池處於STOP狀態,此時線程池不能接受新的任務,並且會去嘗試終止正在執行的任務,返回沒有執行的任務列表;
4,當線程池處於SHUTDOWN或STOP狀態,並且所有工作線程已經銷燬,任務緩存隊列已經清空或執行結束後,線程池被設置爲TERMINATED狀態

參考:http://blog.csdn.net/shakespeare001/article/details/51330745

http://singleant.iteye.com/blog/1423931

http://blog.csdn.net/it_man/article/details/7193727



from:https://blog.csdn.net/tuke_tuke/article/details/51353925


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