執行器中控制任務完成
執行器中控制任務完成
Java API提供FutureTask類作為一種可撤銷非同步計算,此類實現了Runnable和Future介面並提供Future介面的基礎實現。通過使用Callable或Runnable(Runnable物件不返回結果,所以假若Future物件返回結果的話,就需要傳參)物件建立FutureTask類。此類提供取消執行以及獲得計算結果的方法,還提供名為done()的方法允許在執行器中的任務執行完成後再執行一些程式碼。此類用來進行後處理操作,例如生成報告,通過電子郵件傳送結果,或者釋放資源。當FutureTask物件正在控制的任務執行完成時,done()方法被FutureTask類內部呼叫。此方法在任務結果被設定並且其狀態變為isDone之後呼叫,而不考慮任務是否已經被正常地取消或者結束。
預設情況下,此方法為空,通過重寫FutureTask類和實現此方法改變特性。在本節中,學習如何重寫此方法在任務執行之後執行程式碼。
準備工作
本範例通過Eclipse開發工具實現。如果使用諸如NetBeans的開發工具,開啟並建立一個新的Java專案。
實現過程
通過如下步驟完成範例:
-
建立名為ExecutableTask的類,實現String類引數化的Callable介面:
public class ExecutableTask implements Callable<String>{
-
定義名為name的私有String屬性,儲存任務名稱。實現getName()方法,返回屬性值:
private final String name; public String getName() { return name; }
-
實現類建構函式,初始化任務名稱:
public ExecutableTask(String name){ this.name = name; }
-
實現call()方法,設定任務隨機休眠一段時間,並返回任務名稱的資訊:
@Override public String call() throws Exception { try{ long duration = (long)(Math.random() * 10
-
實現名為ResultTask的類,繼承String類引數化的FutureTask類:
public class ResultTask extends FutureTask<String>{
-
定義名為name的私有String屬性,儲存任務名稱:
private final String name;
-
實現類建構函式,需要接收Callable物件作為引數。呼叫父類的建構函式並使用任務接收到的屬性初始化名稱屬性值:
public ResultTask(ExecutableTask callable) { super(callable); this.name = callable.getName(); }
-
重寫done()方法,判斷isCancelled()方法的值,根據返回值,輸出不同的資訊到控制檯:
@Override protected void done() { if(isCancelled()) { System.out.printf("%s : Has been canceled\n" , name); } else { System.out.printf("%s : Has finished\n", name); } }
-
實現範例的主方法,建立一個包含main()方法的Main類:
public class Main { public static void main(String[] args) {
-
使用Executors類的newCachedThreadPool()方法建立ExecutorService:
ExecutorService executor = Executors.newCachedThreadPool();
-
建立陣列儲存五個ResultTask物件:
ResultTask resultTasks[] = new ResultTask[5];
-
初始化ResultTask物件,對陣列中的每個元素,首先使用物件建立ExecutorTask,然後建立ResultTask。接下來使用submit()方法傳送ResultTask到執行器:
for(int i = 0 ; i < 5 ; i ++){ ExecutableTask executableTask = new ExecutableTask("Task " +i); resultTasks[i] = new ResultTask(executableTask); executor.submit(resultTasks[i]); }
-
設定主執行緒休眠5秒:
try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
-
取消傳送到執行器的所有任務:
for (int i=0; i<resultTasks.length; i++) { resultTasks[i].cancel(true); }
-
使用ResultTask物件的get()方法,輸出未被取消的任務結果到控制檯:
for (int i = 0 ; i < resultTasks.length ; i ++){ try { if(!resultTasks[i].isCancelled()) { System.out.printf("%s\n", resultTasks[i].get()); } } catch (InterruptedException | ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
-
使用shutdown()方法結束執行器:
executor.shutdown(); } }
工作原理
當正在被控制的任務完成其執行時,done()方法被FutureTask類呼叫。本範例中,實現了Callable物件、ExecutableTask類,以及FutureTask類的子類,用來控制ExecutableTask物件的執行。
done()方法在生成結果值且改變任務狀態為isDone之後被FutureTask類內部呼叫。你無法改變任務結果值或者改變其狀態,但是你能夠關閉被任務使用的資源,輸出日誌資訊,或者傳送通知。FutureTask類可以用來確保特殊的任務只能執行一次,因為呼叫其run()方法將只執行封裝的Runnable/Callable介面一次(並且可用的情況下,使用get能夠得到結果)。
更多關注
本章“執行器中執行返回結果的任務“小節。