使用Callable返回結(jié)果
本文是Sun官方以Blog形式發(fā)布的Java核心技術(shù)竅門(JavaCoreTechTip)中的一個(gè)。本文主要介紹了Callable及其相關(guān)接口和類的使用,篇幅不長(zhǎng)且易于理解,故翻譯在了此處,相信對(duì)于準(zhǔn)備或剛接觸java.util.concurrent的朋友會(huì)有所幫助。(2008.05.31最后更新)自從Java平臺(tái)的最開始,Runnable接口就已存在了。它允許你定義一個(gè)可由線程完成的任務(wù)。如大多數(shù)人所已知的那樣,它只提供了一個(gè)run方法,該方法既不接受任何參數(shù),也不返回任何值。如果你需要從一個(gè)未完成的任務(wù)中返回一個(gè)值,你就必須在該接口之外使用一個(gè)方法去等待該任務(wù)完成時(shí)通報(bào)的某種消息。例如,下面的示例就是你在這種情景下可能做的事情:
Runnable runnable = ...;
Thread t = new Thread(runnable);
t.start();
t.join();
String value = someMethodtoGetSavedValue()
嚴(yán)格來(lái)說(shuō),上述代碼并無(wú)錯(cuò)誤,但現(xiàn)在可用不同的方法去做,這要感謝J2SE 5.0引入的Callable接口。不同于Runnable接口擁有run方法,Callable接口提供的是call方法,該方法可以返回一個(gè)Object對(duì)象,或可返回任何一個(gè)在泛型化格式中定義了的特定類型的對(duì)象。
public interface Callable<V> {
V call() throws Exception;
}
因?yàn)槟悴豢赡馨袰allable對(duì)象傳到Thread對(duì)象去執(zhí)行,你可換用ExecutorService對(duì)象去執(zhí)行Callable對(duì)象。該服務(wù)接受Callable對(duì)象,并經(jīng)由submit方法去執(zhí)行它。
<T> Future<T> submit(Callable<T> task)
如該方法的定義所示,提交一個(gè)Callable對(duì)象給ExecutorService會(huì)返回一個(gè)Future對(duì)象。然后,F(xiàn)uture的get方法將會(huì)阻塞,直到任務(wù)完成。
為了證明這一點(diǎn),下面的例子為命令行中的每個(gè)詞都創(chuàng)建一個(gè)單獨(dú)的Callable實(shí)例,然后把這些詞的長(zhǎng)度加起來(lái)。各個(gè)Callable對(duì)象將只是計(jì)算它自己的詞的長(zhǎng)度之和。Futures對(duì)象的Set集合將被保存以便從中獲得計(jì)算用的值。如果需要保持返回值的順序,則可換用一個(gè)List對(duì)象。
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保存了每個(gè)詞并使用該詞的長(zhǎng)度作為call方法的返回值。這個(gè)值可能會(huì)花點(diǎn)兒時(shí)間去生成,不過(guò)在這個(gè)例子中,可以立即知道它。 call方法的唯一要求是這個(gè)值要在call方法的結(jié)尾處返回。當(dāng)Future的get方法稍后被調(diào)用時(shí),如果任務(wù)運(yùn)行得很快的話,F(xiàn)uture將會(huì)自動(dòng)得到這個(gè)值(如同本例的情況),否則將一直等到該值生成完畢為止。多次調(diào)用get方法不會(huì)導(dǎo)致任務(wù)從該線程返回。因?yàn)樵摮绦虻哪康氖怯?jì)劃所有字的長(zhǎng)度之和,它不會(huì)強(qiáng)令Callable任務(wù)結(jié)束。如果最后一個(gè)任務(wù)在前三個(gè)任務(wù)之前完成,也是沒(méi)錯(cuò)的。對(duì)Future的get方法的第一次調(diào)用將只會(huì)等待Set中第一個(gè)任務(wù)結(jié)束,而不會(huì)阻塞其它的任務(wù)分別執(zhí)行完畢。它只會(huì)等待當(dāng)次線程或任務(wù)結(jié)束。這個(gè)特定的例子使用固定數(shù)線程池來(lái)產(chǎn)生ExecutorService對(duì)象,但其它有效的方法也是可行的。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);
}
}
關(guān)于執(zhí)行器和線程池用法的更多信息,請(qǐng)見Java Tutorial中Executors一節(jié)。SwingWorker類是另一個(gè)使用Future的Runnable對(duì)象的例子,盡管有些微不同之處。更多信息請(qǐng)見Java Tutorial中Worker Threads and SwingWorker一節(jié)。
posted on 2008-05-31 22:24 John Jiang 閱讀(3854) 評(píng)論(0) 編輯 收藏 所屬分類: JavaSE 、Java 、Concurrency 、翻譯 、CoreJavaTechTips