使用Callable返回結果
本文是Sun官方以Blog形式發布的Java核心技術竅門(JavaCoreTechTip)中的一個。本文主要介紹了Callable及其相關接口和類的使用,篇幅不長且易于理解,故翻譯在了此處,相信對于準備或剛接觸java.util.concurrent的朋友會有所幫助。(2008.05.31最后更新)自從Java平臺的最開始,Runnable接口就已存在了。它允許你定義一個可由線程完成的任務。如大多數人所已知的那樣,它只提供了一個run方法,該方法既不接受任何參數,也不返回任何值。如果你需要從一個未完成的任務中返回一個值,你就必須在該接口之外使用一個方法去等待該任務完成時通報的某種消息。例如,下面的示例就是你在這種情景下可能做的事情:
Runnable runnable = ...;
Thread t = new Thread(runnable);
t.start();
t.join();
String value = someMethodtoGetSavedValue()
嚴格來說,上述代碼并無錯誤,但現在可用不同的方法去做,這要感謝J2SE 5.0引入的Callable接口。不同于Runnable接口擁有run方法,Callable接口提供的是call方法,該方法可以返回一個Object對象,或可返回任何一個在泛型化格式中定義了的特定類型的對象。
public interface Callable<V> {
V call() throws Exception;
}
因為你不可能把Callable對象傳到Thread對象去執行,你可換用ExecutorService對象去執行Callable對象。該服務接受Callable對象,并經由submit方法去執行它。
<T> Future<T> submit(Callable<T> task)
如該方法的定義所示,提交一個Callable對象給ExecutorService會返回一個Future對象。然后,Future的get方法將會阻塞,直到任務完成。
為了證明這一點,下面的例子為命令行中的每個詞都創建一個單獨的Callable實例,然后把這些詞的長度加起來。各個Callable對象將只是計算它自己的詞的長度之和。Futures對象的Set集合將被保存以便從中獲得計算用的值。如果需要保持返回值的順序,則可換用一個List對象。
import java.util.*;
import java.util.concurrent.*;
public class CallableExample {
public static class WordLengthCallable
implements Callable {
private String word;
public WordLengthCallable(String word) {
this.word = word;
}
public Integer call() {
return Integer.valueOf(word.length());
}
}
public static void main(String args[]) throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(3);
Set<Future<Integer>> set = new HashSet<Future≶Integer>>();
for (String word: args) {
Callable<Integer> callable = new WordLengthCallable(word);
Future<Integer> future = pool.submit(callable);
set.add(future);
}
int sum = 0;
for (Future<Integer> future : set) {
sum += future.get();
}
System.out.printf("The sum of lengths is %s%n", sum);
System.exit(sum);
}
}
WordLengthCallable保存了每個詞并使用該詞的長度作為call方法的返回值。這個值可能會花點兒時間去生成,不過在這個例子中,可以立即知道它。 call方法的唯一要求是這個值要在call方法的結尾處返回。當Future的get方法稍后被調用時,如果任務運行得很快的話,Future將會自動得到這個值(如同本例的情況),否則將一直等到該值生成完畢為止。多次調用get方法不會導致任務從該線程返回。因為該程序的目的是計劃所有字的長度之和,它不會強令Callable任務結束。如果最后一個任務在前三個任務之前完成,也是沒錯的。對Future的get方法的第一次調用將只會等待Set中第一個任務結束,而不會阻塞其它的任務分別執行完畢。它只會等待當次線程或任務結束。這個特定的例子使用固定數線程池來產生ExecutorService對象,但其它有效的方法也是可行的。import java.util.concurrent.*;
public class CallableExample {
public static class WordLengthCallable
implements Callable {
private String word;
public WordLengthCallable(String word) {
this.word = word;
}
public Integer call() {
return Integer.valueOf(word.length());
}
}
public static void main(String args[]) throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(3);
Set<Future<Integer>> set = new HashSet<Future≶Integer>>();
for (String word: args) {
Callable<Integer> callable = new WordLengthCallable(word);
Future<Integer> future = pool.submit(callable);
set.add(future);
}
int sum = 0;
for (Future<Integer> future : set) {
sum += future.get();
}
System.out.printf("The sum of lengths is %s%n", sum);
System.exit(sum);
}
}
關于執行器和線程池用法的更多信息,請見Java Tutorial中Executors一節。SwingWorker類是另一個使用Future的Runnable對象的例子,盡管有些微不同之處。更多信息請見Java Tutorial中Worker Threads and SwingWorker一節。
posted on 2008-05-31 22:24 John Jiang 閱讀(3854) 評論(0) 編輯 收藏 所屬分類: JavaSE 、Java 、Concurrency 、翻譯 、CoreJavaTechTips