Callable与Future
Runnable、Future、Callable的区别
Runnable我们都用过,因为开启一个新的线程的时候,需要传递一个Runnable接口作为执行对象,这个接口里面只包含一个run方法,无参数,无返回值。
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
而Callable和Runnable类似,但是有返回值。Callable里面只有一个方法:
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
想要使用Callable接口需要配合ExecutorService对象(关于ExecutorService类的介绍我们会放到下一篇线程池的讲解中),ExecutorService类中有两个submit方法,一个用来提交Runnable对象,一个用来提交Callable对象。
我们可以来看一下:
public interface ExecutorService extends Executor {
@NotNull <T> Future<T> submit(@NotNull Callable<T> task);
@NotNull <T> Future<T> submit(@NotNull Runnable task,T result);
@NotNull Future<?> submit(@NotNull Runnable task)
}
你会发现,这些方法都是返回一个Future的对象。
Future保存异步计算的结果,可以启动一个计算,将Future对象交给某个线程,然后就可以不用管它了,然后等待结果计算好即可。
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;
}
Future的主要作用就是对具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。
如果选择使用get方法获取任务执行的结果,会产生阻塞,直到计算完成。
带参数的get方法用于指定一个时间长度,如果在这个时间长度内,结果还是没有被计算完成的话,会直接返回Null。
举个例子,用Future获取Callable接口运行的接口:
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
Future<Integer> result = executor.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("The sub thread is executing the task...");
Thread.sleep(3000);
int sum = 0;
for (int i = 0; i < 100; i++)
sum += i;
return sum;
}
});
executor.shutdown();//shutdown调用后,不可以再submit新的task,已经submit的将继续执行。
try {
Thread.sleep(1000);//主线程等待1秒
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("The main thread is executing the task...");
try {
System.out.println("The result of sub task calculated is " + result.get());
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("All task have been finished!");
}
运行结果:
The sub thread is executing the task...
The main thread is executing the task...
The result of sub task calculated is 4950
All task have been finished!
Process finished with exit code 0
FutureTask接口
三个接口都介绍完了,这时候我们得说一个有趣的东西,也就是我们的重头菜:FutureTask。
先不介绍它是什么,让我们直接来看看它的定义:
public class FutureTask<V> implements RunnableFuture<V>
OK,我们发现它实现了一个RunnableFuture接口,这个接口是什么呢?我们再来看一下:
public interface RunnableFuture<V> extends Runnable, Future<V>
好吧,原来RunnableFuture既实现了Runnable又实现了Future,这名字真好理解…
那FutureTask又实现了RunnableFuture接口,So,Runnable接口既能够被作为线程接收的Runnable对象被运行,也能够作为Future对象接收Callable对象的返回值。
我们来看一下,FutureTask的两个构造方法:
public class FutureTask<V> implements RunnableFuture<V> {
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
}
使用FutureTask接口获取Callable的数据
public class Main {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
//创建FutureTask接口对象,并接受一个Callable参数
FutureTask<Integer> futureTask = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("The sub thread is executing the task...");
Thread.sleep(3000);
int sum = 0;
for (int i = 0; i < 100; i++)
sum += i;
return sum;
}
});
executor.submit(futureTask);//提交要运行的任务
executor.shutdown();//shutdown调用后,不可以再submit新的task,已经submit的将继续执行。
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("The main thread is executing the task...");
try {
System.out.println("The result of sub task calculated is " + futureTask.get());
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("All task have been finished!");
}
}
执行结果一样:
The sub thread is executing the task...
The main thread is executing the task...
The result of sub task calculated is 4950
All task have been finished!
Process finished with exit code 0