原文地址:http://www.geeksforgeeks.org/callable-future-java/
Callable的用处
在Java中创建线程有两种方法——一种是继承Thread类,另一种是实现Runnable接口。但是Runnable没有的一个特点就是它不能在线程结束的时候返回结果,例如当run()结束了。Java中的Callable接口正好支持这个特征。
Callable vs Runnable
- 对于Runnable的实现,run()方法也得必须的实现,但是它不能返回任何东西,但是在Callable中,实现call()的话就可以在结束时返回结果。注意不能用Callable创建线程,线程只能用Runnable来创建。
- 另外的一个不同之处就是call()方法可以抛异常,但是run()不行。
实现Callable必须重载方法签名。
public Object call() throws Exception;
这里是一个Callable例子的代码,它在每0-4秒的延迟间隔会返回一个随机数。
// Java program to illustrate Callable
// to return a random number
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class CallableExample implements Callable {
public Object call() throws Exception {
// Create random number generator
Random generator = new Random();
Integer randomNumber = generator.nextInt(5);
// To simulate a heavy computation,
// we delay the thread for some random time
Thread.sleep(randomNumber * 1000);
return randomNumber;
}
}
Future
当一个call()方法执行完成的时候,结果就必须保存在main线程已知的对象中,这样main线程就可以知道这个线程返回的结果是什么。那么代码在后来是如何保存并获取这个结果的呢?对于这个问题,就可以用Future对象了。考虑一个Future对象得到这个结果——它可能不会立马拿到,但是将来(Future,一旦Callable返回)也会拿到。因此,一个Future实际就是main线程的一个路径,可以对其他线程的进度和结果进行跟踪。想要实现这个接口,有5个方法必须得重载,但是在下面的例子中用了一个具体的库实现,只有几个重要的方法列在了这里。
注意到Callable和Future做的是两种不同的事情——Callable类似于Runnable,它封装了任务并运行在其他的线程上,因此Future是用于从其他线程获取并存储结果。实际上,Future可以跟Runnable做的工作是一样的,这就使得在Executors出现的时候情况就变得更加清晰了。
- public boolean cancel(boolean mayInterrupt):用于停止任务。如果一个任务没有启动,那就停止它。如果已经启动了,并且mayInterrupt为true,那么就中断这个任务。
- public Object get() throws InterruptedException, ExecutionException: 用于得到任务的结果。如果任务完成了,它就立即返回结果,否则就等待任务的完成然后返回结果。
- public boolean isDone():如果一个任务完成了就返回true,否则返回false。
想创建线程,那就需要Runnable,想要得到结果,那就用Future。
Java库中有FutureType具体的类型,它实现了Runnable和Future,同时结合了两边的便捷。
FutureTask对象可以通过它的构造函数和Callable来创建,然后FutureTask将提供给Thread的构造函数来创建一个Thread对象。因此,Callable间接地创建了线程。再一次强调,Callable是不可能直接创建线程的。
这里是用Callable和FutureTask的例子代码:
// Java program to illustrate Callable and FutureTask
// for random number generation
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class CallableExample implements Callable {
public Object call() throws Exception {
Random generator = new Random();
Integer randomNumber = generator.nextInt(5);
Thread.sleep(randomNumber * 1000);
return randomNumber;
}
}
public class CallableFutureTest {
public static void main(String[] args) throws Exception {
// FutureTask is a concrete class that
// implements both Runnable and Future
FutureTask[] randomNumberTasks = new FutureTask[5];
for (int i = 0; i < 5; i++) {
Callable callable = new CallableExample();
// Create the FutureTask with Callable
randomNumberTasks[i] = new FutureTask(callable);
// As it implements Runnable, create Thread
// with FutureTask
Thread t = new Thread(randomNumberTasks[i]);
t.start();
}
for (int i = 0; i < 5; i++) {
// As it implements Future, we can call get()
System.out.println(randomNumberTasks[i].get());
// This method blocks till the result is obtained
// The get method can throw checked exceptions
// like when it is interrupted. This is the reason
// for adding the throws clause to main
}
}
}
输出:
4
2
3
3
0
启动之后与线程的所有交互都是用实现Future接口的FutureTask对象。因此就没必要保存Thread对象了。利用FutureTask对象,可以取消任务,检查任务是否已经完成并尝试得到结果。
下面是只用Runnable的代码。
// Java program to illustrate Runnable
// for random number generation
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class RunnableExample implements Runnable {
// Shared object to store result
private Object result = null;
public void run() {
Random generator = new Random();
Integer randomNumber = generator.nextInt(5);
// As run cannot throw any Exception
try {
Thread.sleep(randomNumber * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Store the return value in result when done
result = randomNumber;
// Wake up threads blocked on the get() method
synchronized (this) {
notifyAll();
}
}
public synchronized Object get() throws InterruptedException {
while (result == null)
wait();
return result;
}
}
// Code is almost same as the previous example with a
// few changes made to use Runnable instead of Callable
public class RunnableTest {
public static void main(String[] args) throws Exception {
RunnableExample[] randomNumberTasks = new RunnableExample[5];
for (int i = 0; i < 5; i++) {
randomNumberTasks[i] = new RunnableExample();
Thread t = new Thread(randomNumberTasks[i]);
t.start();
}
for (int i = 0; i < 5; i++)
System.out.println(randomNumberTasks[i].get());
}
}
输出示例:
0
4
3
1
4
2
参考文献