Java中的Runnable、Callable和Future的关系

前提

Java中多线程包下相关的接口有RunnableCallableFuture

  • Runnable

    1
    2
    3
    public interface Runnable {
    void run();
    }
  • Callable

    1
    2
    3
    public interface Callable<V> {
    V call() throws Exception;
    }

Runnable和Callable的区别

  1. Runnable执行方法是run(),Callable是call()
  2. Runnable无返回值且不会抛错;Callable带返回值且可以向外抛异常

这两个只是个接口,并不能用来进行线程相关操作。开辟线程还是要用Thread,而Thread又只能传Runnable,不支持传Callable,怎么破?

解决方法:创建一个类,让他实现Runnable接口,然后内部持有一个Callable对象。在Runnable的run方法中调用mCallable.call()方法。

妙啊~

这个类叫FutureTask

FutureTask

类图

img_name

用法

1
2
3
4
5
6
7
8
9
10
11
12
Callable<String> callable = new Callable<String>() {
public String call() throws Exception {
//something to do...
return "睡醒了";
}
};
FutureTask<String> task = new FutureTask<String>(callable);
//FutureTask实现了Runnable接口,所以可以直接传给Thread
new Thread(task).start();
//获取到了返回值
String call = task.get();
System.out.println(call);

Future

可以看到FutureTask没有直接实现Runnable,而是继承RunnableFutureRunnableFuture来同时实现了Runnable和Future两个接口:

1
2
3
4
5
6
7
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}

且看Future接口:

1
2
3
4
5
6
7
8
9
10
11
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);

boolean isCancelled();

boolean isDone();

V get() throws InterruptedException, ExecutionException;

V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

说明FutureTask还额外实现了Future接口的相关功能。比如说Runnable的run方法中无返回值,要取到返回值的话就要用到Future中的get方法。

到这里三者的关系应该捋清楚了。

Runnable没返回值,Callable有返回值,但是Thread不接受Callable,所以有了FutureTaskFutureTask实现Runnable,传入Callable对象。这个时候还需要写一些相关的方法(比如取出返回值的方法),然后这些方法就收录到了Future接口中。

Future的另一个使用场景

线程池

线程池中线程来了又去,总有一个需要带返回值。

这个时候就要传入Callable了。

1
2
3
4
5
6
7
8
9
10
Callable<String> callable = new Callable<String>() {
public String call() throws Exception {
//something to do...
return "睡醒了";
}
};
ExecutorService executor = Executors.newFixedThreadPool(2);
Future<String> task = executor.submit(callable);
String call = task.get();
System.out.println(call);

executor.submit(callable)返回的也是一个Future ,然后就可以获取到返回值了。

ps:其实内部调用的还是FutureTask,submit代码如下:

1
2
3
4
5
6
7
8
9
10
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}

protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}

另补充一点:FutureTask()也可以传入Runnable,不过最后还是会把Runnable转换成Callable的😂

用的这个:

1
2
3
4
5
6
7
8
9
10
11
12
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}