《java併發編程實戰》第六章 Executor和ExecutorService總結

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

ExecutorService 樣例

這裏有壹個簡單的使用Java 實現的 ExectorService 樣例:

ExecutorService executorService = Executors.newFixedThreadPool(10);  
  
executorService.execute(new Runnable() {  
    public void run() {  
        System.out.println("Asynchronous task");  
    }  
});  
  
executorService.shutdown();  

   首先使用 newFixedThreadPool() 工廠方法創建壹個 ExecutorService ,上述代碼創建了壹個可以容納10個線程任務的線程池。其次,向 execute() 方法中傳遞壹個異步的 Runnable 接口的實現,這樣做會讓 ExecutorService 中的某個線程執行這個 Runnable 線程。

任務的委託(Task Delegation)

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

ExecutorService 的實現

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

創建壹個 ExecutorService

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

ExecutorService executorService1 = Executors.newSingleThreadExecutor();  
ExecutorService executorService2 = Executors.newFixedThreadPool(10);  
ExecutorService executorService3 = Executors.newScheduledThreadPool(10); 

ExecutorService 使用方法

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

execute(Runnable)  
submit(Runnable)  
submit(Callable)  
invokeAny(...)  
invokeAll(...) 
我會在接下來的內容裏把每個方法都看壹遍。

execute(Runnable)

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

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

submit(Runnable)

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

Future future = executorService.submit(new Runnable() {  
    public void run() {  
        System.out.println("Asynchronous task");  
    }  
});  
//如果任務結束執行則返回 null  
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 的樣例:

Future future = executorService.submit(new Callable(){  
    public Object call() throws Exception {  
        System.out.println("Asynchronous Callable");  
        return "Callable Result";  
    }  
});  
  
System.out.println("future.get() = " + future.get()); 
上述樣例代碼會輸出如下結果:

ExecutorService executorService = Executors.newSingleThreadExecutor();  
  
Set<Callable<String>> callables = new HashSet<Callable<String>>();  
  
callables.add(new Callable<String>() {  
    public String call() throws Exception {  
        return "Task 1";  
    }  
});  
callables.add(new Callable<String>() {  
    public String call() throws Exception {  
        return "Task 2";  
    }  
});  
callables.add(new Callable<String>() {  
    public String call() throws Exception {  
        return "Task 3";  
    }  
});  
  
String result = executorService.invokeAny(callables);  
  
System.out.println("result = " + result);  
  
executorService.shutdown(); 

inVokeAny()

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

ExecutorService executorService = Executors.newSingleThreadExecutor();  
  
Set<Callable<String>> callables = new HashSet<Callable<String>>();  
  
callables.add(new Callable<String>() {  
    public String call() throws Exception {  
        return "Task 1";  
    }  
});  
callables.add(new Callable<String>() {  
    public String call() throws Exception {  
        return "Task 2";  
    }  
});  
callables.add(new Callable<String>() {  
    public String call() throws Exception {  
        return "Task 3";  
    }  
});  
  
String result = executorService.invokeAny(callables);  
  
System.out.println("result = " + result);  
  
executorService.shutdown(); 
以上樣例代碼會打印出在給定的集合中的某壹個 Callable 的返回結果。我嘗試運行了幾次,結果都在改變。有時候返回結果是"Task 1",有時候是"Task 2",等等。

invokeAll()

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

ExecutorService executorService = Executors.newSingleThreadExecutor();  
  
Set<Callable<String>> callables = new HashSet<Callable<String>>();  
  
callables.add(new Callable<String>() {  
    public String call() throws Exception {  
        return "Task 1";  
    }  
});  
callables.add(new Callable<String>() {  
    public String call() throws Exception {  
        return "Task 2";  
    }  
});  
callables.add(new Callable<String>() {  
    public String call() throws Exception {  
        return "Task 3";  
    }  
});  
  
String result = executorService.invokeAny(callables);  
  
System.out.println("result = " + result);  
  
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,轉載請註明原始出處。


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