ExecutorService 的理解與使用

商城中用到郵件發送報表。使用異步機制在後臺完成郵件發送。

這個類就可以很好的完成這個功能



接口 java.util.concurrent.ExecutorService 表述了異步執行的機制,並且可以讓任務在後臺執行。壹個 ExecutorService 實例因此特別像壹個線程池。事實上,在 java.util.concurrent 包中的 ExecutorService 的實現就是壹個線程池的實現。

ExecutorService 樣例

這裏有壹個簡單的使用Java 實現的 ExectorService 樣例:
[java] view plain copy
  1. ExecutorService executorService = Executors.newFixedThreadPool(10);  
  2.   
  3. executorService.execute(new Runnable() {  
  4.     public void run() {  
  5.         System.out.println("Asynchronous task");  
  6.     }  
  7. });  
  8.   
  9. executorService.shutdown();  
首先使用 newFixedThreadPool() 工廠方法創建壹個 ExecutorService ,上述代碼創建了壹個可以容納10個線程任務的線程池。其次,向 execute() 方法中傳遞壹個異步的 Runnable 接口的實現,這樣做會讓 ExecutorService 中的某個線程執行這個 Runnable 線程。

任務的委託(Task Delegation)

下方展示了一個線程的把任務委託異步執行的ExecutorService的示意圖。

壹旦線程把任務委託給 ExecutorService,該線程就會繼續執行與運行任務無關的其它任務。

ExecutorService 的實現

由於 ExecutorService 只是壹個接口,你壹量需要使用它,那麼就需要提供壹個該接口的實現。ExecutorService 接口在 java.util.concurrent 包中有如下實現類:

創建壹個 ExecutorService

你可以根據自己的需要來創建壹個 ExecutorService ,也可以使用 Executors 工廠方法來創建壹個 ExecutorService 實例。這裏有幾個創建 ExecutorService 的例子:

[java] view plain copy
  1. ExecutorService executorService1 = Executors.newSingleThreadExecutor();  
  2. ExecutorService executorService2 = Executors.newFixedThreadPool(10);  
  3. ExecutorService executorService3 = Executors.newScheduledThreadPool(10);  

ExecutorService 使用方法

這裏有幾種不同的方式讓你將任務委託給壹個 ExecutorService:

[java] view plain copy
  1. execute(Runnable)  
  2. submit(Runnable)  
  3. submit(Callable)  
  4. invokeAny(...)  
  5. invokeAll(...)  
我會在接下來的內容裏把每個方法都看壹遍。

execute(Runnable)

方法 execute(Runnable) 接收壹個 java.lang.Runnable 對象作爲參數,並且以異步的方式執行它。如下是壹個使用 ExecutorService 執行 Runnable 的例子:

[java] view plain copy
  1. ExecutorService executorService = Executors.newSingleThreadExecutor();  
  2.   
  3. executorService.execute(new Runnable() {  
  4.     public void run() {  
  5.         System.out.println("Asynchronous task");  
  6.     }  
  7. });  
  8.       
  9. executorService.shutdown();  
使用這種方式沒有辦法獲取執行 Runnable 之後的結果,如果你希望獲取運行之後的返回值,就必須使用 接收 Callable 參數的 execute() 方法,後者將會在下文中提到。

submit(Runnable)

方法 submit(Runnable) 同樣接收壹個 Runnable 的實現作爲參數,但是會返回壹個 Future 對象。這個 Future 對象可以用於判斷 Runnable 是否結束執行。如下是壹個 ExecutorService 的 submit() 方法的例子:

[java] view plain copy
  1. Future future = executorService.submit(new Runnable() {  
  2.     public void run() {  
  3.         System.out.println("Asynchronous task");  
  4.     }  
  5. });  
  6. //如果任務結束執行則返回 null  
  7. System.out.println("future.get()=" + future.get());  

submit(Callable)

方法 submit(Callable) 和方法 submit(Runnable) 比較類似,但是區別則在於它們接收不同的參數類型。Callable 的實例與 Runnable 的實例很類似,但是 Callable 的 call() 方法可以返回壹個結果。方法 Runnable.run() 則不能返回結果。

Callable 的返回值可以從方法 submit(Callable) 返回的 Future 對象中獲取。如下是壹個 ExecutorService Callable 的樣例:

[java] view plain copy
  1. Future future = executorService.submit(new Callable(){  
  2.     public Object call() throws Exception {  
  3.         System.out.println("Asynchronous Callable");  
  4.         return "Callable Result";  
  5.     }  
  6. });  
  7.   
  8. System.out.println("future.get() = " + future.get());  

上述樣例代碼會輸出如下結果:
[java] view plain copy
  1. ExecutorService executorService = Executors.newSingleThreadExecutor();  
  2.   
  3. Set<Callable<String>> callables = new HashSet<Callable<String>>();  
  4.   
  5. callables.add(new Callable<String>() {  
  6.     public String call() throws Exception {  
  7.         return "Task 1";  
  8.     }  
  9. });  
  10. callables.add(new Callable<String>() {  
  11.     public String call() throws Exception {  
  12.         return "Task 2";  
  13.     }  
  14. });  
  15. callables.add(new Callable<String>() {  
  16.     public String call() throws Exception {  
  17.         return "Task 3";  
  18.     }  
  19. });  
  20.   
  21. String result = executorService.invokeAny(callables);  
  22.   
  23. System.out.println("result = " + result);  
  24.   
  25. executorService.shutdown();  

inVokeAny()

方法 invokeAny() 接收壹個包含 Callable 對象的集合作爲參數。調用該方法不會返回 Future 對象,而是返回集合中某壹個 Callable 對象的結果,而且無法保證調用之後返回的結果是哪壹個 Callable,只知道它是這些 Callable 中壹個執行結束的 Callable 對象。
如果壹個任務運行完畢或者拋出異常,方法會取消其它的 Callable 的執行。
以下是壹個樣例:

[java] view plain copy
  1. ExecutorService executorService = Executors.newSingleThreadExecutor();  
  2.   
  3. Set<Callable<String>> callables = new HashSet<Callable<String>>();  
  4.   
  5. callables.add(new Callable<String>() {  
  6.     public String call() throws Exception {  
  7.         return "Task 1";  
  8.     }  
  9. });  
  10. callables.add(new Callable<String>() {  
  11.     public String call() throws Exception {  
  12.         return "Task 2";  
  13.     }  
  14. });  
  15. callables.add(new Callable<String>() {  
  16.     public String call() throws Exception {  
  17.         return "Task 3";  
  18.     }  
  19. });  
  20.   
  21. String result = executorService.invokeAny(callables);  
  22.   
  23. System.out.println("result = " + result);  
  24.   
  25. executorService.shutdown();  
以上樣例代碼會打印出在給定的集合中的某壹個 Callable 的返回結果。我嘗試運行了幾次,結果都在改變。有時候返回結果是"Task 1",有時候是"Task 2",等等。

invokeAll()

方法 invokeAll() 會調用存在於參數集合中的所有 Callable 對象,並且返回壹個包含 Future 對象的集合,你可以通過這個返回的集合來管理每個 Callable 的執行結果。
需要注意的是,任務有可能因爲異常而導致運行結束,所以它可能並不是真的成功運行了。但是我們沒有辦法通過 Future 對象來瞭解到這個差異。
以下是壹個代碼樣例:

[java] view plain copy
  1. ExecutorService executorService = Executors.newSingleThreadExecutor();  
  2.   
  3. Set<Callable<String>> callables = new HashSet<Callable<String>>();  
  4.   
  5. callables.add(new Callable<String>() {  
  6.     public String call() throws Exception {  
  7.         return "Task 1";  
  8.     }  
  9. });  
  10. callables.add(new Callable<String>() {  
  11.     public String call() throws Exception {  
  12.         return "Task 2";  
  13.     }  
  14. });  
  15. callables.add(new Callable<String>() {  
  16.     public String call() throws Exception {  
  17.         return "Task 3";  
  18.     }  
  19. });  
  20.   
  21. String result = executorService.invokeAny(callables);  
  22.   
  23. System.out.println("result = " + result);  
  24.   
  25. executorService.shutdown();  

ExecuteService 服務的關閉

當使用 ExecutorService 完畢之後,我們應該關閉它,這樣才能保證線程不會繼續保持運行狀態。 
舉例來說,如果你的程序通過 main() 方法啓動,並且主線程退出了你的程序,如果你還有壹個活動的 ExecutorService 存在於你的程序中,那麼程序將會繼續保持運行狀態。存在於 ExecutorService 中的活動線程會阻止Java虛擬機關閉。
爲了關閉在 ExecutorService 中的線程,你需要調用 shutdown() 方法。ExecutorService 並不會馬上關閉,而是不再接收新的任務,壹但所有的線程結束執行當前任務,ExecutorServie 纔會真的關閉。所有在調用 shutdown() 方法之前提交到 ExecutorService 的任務都會執行。
如果你希望立即關閉 ExecutorService,你可以調用 shutdownNow() 方法。這個方法會嘗試馬上關閉所有正在執行的任務,並且跳過所有已經提交但是還沒有運行的任務。但是對於正在執行的任務,是否能夠成功關閉它是無法保證的,有可能他們真的被關閉掉了,也有可能它會壹直執行到任務結束。這是壹個最好的嘗試。

本文英文原文鏈接:http://tutorials.jenkov.com/java-util-concurrent/executorservice.html#executorservice-example ,中文譯文首發開源中國社區 http://my.oschina.net/bairrfhoinn/blog/177639,轉載請註明原始出處。

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