Java中的Callable和Future

来源:互联网 发布:淘宝网服装货源 编辑:程序博客网 时间:2024/06/07 14:28

原文地址: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 numberimport 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 generationimport 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        }    }}

输出:

42330

启动之后与线程的所有交互都是用实现Future接口的FutureTask对象。因此就没必要保存Thread对象了。利用FutureTask对象,可以取消任务,检查任务是否已经完成并尝试得到结果。

下面是只用Runnable的代码。

// Java program to illustrate Runnable// for random number generationimport 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 Callablepublic 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());    }}

输出示例:

043142

参考文献

  • https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Callable.html
  • https://docs.oracle.com/javase/7/docs/api/java/lang/Runnable.html
  • https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/FutureTask.html
原创粉丝点击