接口 java.util.concurrent.ExecutorService
表述了異步執行的機制,并且可以讓任務在后臺執行。壹個 ExecutorService 實例因此特別像壹個線程池。事實上,在
java.util.concurrent 包中的 ExecutorService 的實現就是壹個線程池的實現。
ExecutorService 樣例
這里有壹個簡單的使用Java 實現的 ExectorService 樣例:
1 | ExecutorService executorService = Executors.newFixedThreadPool( 10 ); |
3 | executorService.execute( new Runnable() { |
5 | System.out.println( "Asynchronous task" ); |
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 的例子:
1 | ExecutorService executorService1 = Executors.newSingleThreadExecutor(); |
2 | ExecutorService executorService2 = Executors.newFixedThreadPool( 10 ); |
3 | ExecutorService executorService3 = Executors.newScheduledThreadPool( 10 ); |
ExecutorService 使用方法
這里有幾種不同的方式讓你將任務委托給壹個 ExecutorService:
我會在接下來的內容里把每個方法都看壹遍。
execute(Runnable)
方法 execute(Runnable) 接收壹個 java.lang.Runnable 對象作為參數,并且以異步的方式執行它。如下是壹個使用 ExecutorService 執行 Runnable 的例子:
1 | ExecutorService executorService = Executors.newSingleThreadExecutor(); |
3 | executorService.execute( new Runnable() { |
5 | System.out.println( "Asynchronous task" ); |
9 | executorService.shutdown(); |
使用這種方式沒有辦法獲取執行 Runnable 之后的結果,如果你希望獲取運行之后的返回值,就必須使用 接收 Callable 參數的 execute() 方法,后者將會在下文中提到。
submit(Runnable)
方法 submit(Runnable) 同樣接收壹個 Runnable 的實現作為參數,但是會返回壹個 Future 對象。這個
Future 對象可以用于判斷 Runnable 是否結束執行。如下是壹個 ExecutorService 的 submit() 方法的例子:
1 | Future future = executorService.submit( new Runnable() { |
3 | System.out.println( "Asynchronous task" ); |
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 的樣例:
1 | Future future = executorService.submit( new Callable(){ |
2 | public Object call() throws Exception { |
3 | System.out.println( "Asynchronous Callable" ); |
4 | return "Callable Result" ; |
8 | System.out.println( "future.get() = " + future.get()); |
上述樣例代碼會輸出如下結果:
2 | future.get() = Callable Result |
inVokeAny()
方法 invokeAny() 接收壹個包含 Callable 對象的集合作為參數。調用該方法不會返回 Future
對象,而是返回集合中某壹個 Callable 對象的結果,而且無法保證調用之后返回的結果是哪壹個 Callable,只知道它是這些
Callable 中壹個執行結束的 Callable 對象。
如果壹個任務運行完畢或者拋出異常,方法會取消其它的 Callable 的執行。
以下是壹個樣例:
01 | ExecutorService executorService = Executors.newSingleThreadExecutor(); |
03 | Set<Callable<String>> callables = new HashSet<Callable<String>>(); |
05 | callables.add( new Callable<String>() { |
06 | public String call() throws Exception { |
10 | callables.add( new Callable<String>() { |
11 | public String call() throws Exception { |
15 | callables.add( new Callable<String>() { |
16 | public String call() throws Exception { |
21 | String result = executorService.invokeAny(callables); |
23 | System.out.println( "result = " + result); |
25 | executorService.shutdown(); |
以上樣例代碼會打印出在給定的集合中的某壹個 Callable 的返回結果。我嘗試運行了幾次,結果都在改變。有時候返回結果是"Task 1",有時候是"Task 2",等等。
invokeAll()
方法 invokeAll() 會調用存在于參數集合中的所有 Callable 對象,并且返回壹個包含 Future 對象的集合,你可以通過這個返回的集合來管理每個 Callable 的執行結果。
需要注意的是,任務有可能因為異常而導致運行結束,所以它可能并不是真的成功運行了。但是我們沒有辦法通過 Future 對象來了解到這個差異。
以下是壹個代碼樣例:
01 | ExecutorService executorService = Executors.newSingleThreadExecutor(); |
03 | Set<Callable<String>> callables = new HashSet<Callable<String>>(); |
05 | callables.add( new Callable<String>() { |
06 | public String call() throws Exception { |
10 | callables.add( new Callable<String>() { |
11 | public String call() throws Exception { |
15 | callables.add( new Callable<String>() { |
16 | public String call() throws Exception { |
21 | List<Future<String>> futures = executorService.invokeAll(callables); |
23 | for (Future<String> future : futures){ |
24 | System.out.println( "future.get = " + future.get()); |
27 | executorService.shutdown(); |
ExecuteService 服務的關閉
當使用 ExecutorService 完畢之后,我們應該關閉它,這樣才能保證線程不會繼續保持運行狀態。
舉例來說,如果你的程序通過 main() 方法啟動,并且主線程退出了你的程序,如果你還有壹個活動的 ExecutorService
存在于你的程序中,那么程序將會繼續保持運行狀態。存在于 ExecutorService 中的活動線程會阻止Java虛擬機關閉。
為了關閉在 ExecutorService 中的線程,你需要調用 shutdown() 方法。ExecutorService
并不會馬上關閉,而是不再接收新的任務,壹但所有的線程結束執行當前任務,ExecutorServie 才會真的關閉。所有在調用
shutdown() 方法之前提交到 ExecutorService 的任務都會執行。
如果你希望立即關閉 ExecutorService,你可以調用 shutdownNow()
方法。這個方法會嘗試馬上關閉所有正在執行的任務,并且跳過所有已經提交但是還沒有運行的任務。但是對于正在執行的任務,是否能夠成功關閉它是無法保證
的,有可能他們真的被關閉掉了,也有可能它會壹直執行到任務結束。這是壹個最好的嘗試。
本文英文原文鏈接:http://tutorials./java-util-concurrent/executorservice.html#executorservice-example ,中文譯文首發開源中國社區 http://my.oschina.net/bairrfhoinn/blog/177639,轉載請注明原始出處。