使用Callable返回结果
自从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将会立刻得到这个值(如同本例的情况),否则将一直等到该值生成完毕为止。
对Future的get方法的调用会阻塞当前线程并等待任务结束,而不会阻塞其它的任务和线程。
这个特定的例子使用固定数线程池来产生 ExecutorService对象,但其它有效的方法也是可行的。另外,注意后提交的任务可能比前提交的任务先完成