1. 程式人生 > >執行緒執行者(十)執行者控制一個任務完成

執行緒執行者(十)執行者控制一個任務完成

宣告:本文是《 Java 7 Concurrency Cookbook 》的第四章,作者: Javier Fernández González     譯者:許巧輝     校對:方騰飛,葉磊

執行者控制一個任務完成

FutureTask類提供一個done()方法,允許你在執行者執行任務完成後執行一些程式碼。你可以用來做一些後處理操作,生成一個報告,通過e-mail傳送結果,或釋放一些資源。當執行的任務由FutureTask來控制完成,FutureTask會內部呼叫這個方法。這個方法在任務的結果設定和它的狀態變成isDone狀態之後被呼叫,不管任務是否已經被取消或正常完成。

預設情況下,這個方法是空的。你可以重寫FutureTask類實現這個方法來改變這種行為。在這個指南中,你將學習如何重寫這個方法,在任務完成之後執行程式碼。

準備工作…

這個指南的例子使用Eclipse IDE實現。如果你使用Eclipse或其他IDE,如NetBeans,開啟它並建立一個新的Java專案。

如何做…

按以下步驟來實現的這個例子:

1.建立ExecutableTask類,並指定其實現Callable介面,引數化為String型別。

public class ExecutableTask implements Callable<String> {

2.宣告一個私有的、型別為String、名為name的屬性,用來儲存任務的名稱。實現getName()方法,返回這個屬性值。

private String name;
public String getName(){
return name;
}

3.實現這個類的構造器,初始化任務的名稱。

public ExecutableTask(String name){
this.name=name;
}

4.實現call()方法。使這個任務睡眠一個隨機時間,返回任務名稱的資訊。

@Override
public String call() throws Exception {
try {
long duration=(long)(Math.random()*10);
System.out.printf("%s: Waiting %d seconds for results.\
n",this.name,duration);
TimeUnit.SECONDS.sleep(duration);
} catch (InterruptedException e) {
}
return "Hello, world. I'm "+name;
}

5.實現ResultTask類,繼承FutureTask類,引數化為String型別。

public class ResultTask extends FutureTask<String> {

6.宣告一個私有的、型別為String、名為name的屬性,用來儲存任務的名稱。

private String name;

7.實現這個類的構造器。它接收一個Callable物件引數。呼叫父類構造器,使用接收到的任務的屬性初始化name屬性。

public ResultTask(Callable<String> callable) {
super(callable);
this.name=((ExecutableTask)callable).getName();
}

8.重寫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);
}
}

9.實現示例的主類,建立Main類,實現main()方法。

public class Main {
public static void main(String[] args) {

10.使用Executors類的newCachedThreadPool()方法建立ExecutorService。

ExecutorService executor=(ExecutorService)Executors.newCachedThreadPool();

11.建立儲存5個ResultTask物件的一個數組。

ResultTask resultTasks[]=new ResultTask[5];

12.初始化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]);
}

13.令主執行緒睡眠5秒。

try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e1) {
e1.printStackTrace();
}

14.取消你提交給執行者的所有任務。

for (int i=0; i<resultTasks.length; i++) {
resultTasks[i].cancel(true);
}

15.將沒有被使用ResultTask物件的get()方法取消的任務的結果寫入到控制檯。

for (int i=0; i<resultTasks.length; i++) {
try {
if (!resultTasks[i].isCanceled()){
System.out.printf("%s\n",resultTasks[i].get());
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}

}

16.使用shutdown()方法關閉執行者。

executor.shutdown();
}
}

它是如何工作的…

當控制任務執行完成後,FutureTask類呼叫done()方法。在這個示例中,你已經實現一個Callable物件,ExecutableTask類,然後一個FutureTask類的子類用來控制ExecutableTask物件的執行。

在建立返回值和改變任務的狀態為isDone狀態後,done()方法被FutureTask類內部呼叫。你不能改變任務的結果值和它的狀態,但你可以關閉任務使用的資源,寫日誌訊息,或傳送通知。

參見

  • 在第4章,執行緒執行者中的執行者執行返回結果的任務指南