前言
我们无论使用Thread还是Runnable新建线程,它都无法返回结果值。而自从Jdk1.5开始,官方提供了 Callable和Future, 通过他们就可以获得任务的执行结果。其中FutureTask则作为Future的实现类。
1. Callable
谈到Callable,我们就得顺便提一下它与Runnable接口的区别。
先说一下java.lang.Runnable吧,它是一个接口,在它里面只声明了一个run()方法:(属于函数式接口)
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
它的run() 方法没有返回值,且不能抛出异常。
而Callable则位于java.util.concurrent 包下,它也是一个函数式接口,声明了call() , 不过该方法可以有返回值还可以抛出异常。
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
在使用上,我们可以参照上面的继承图。通过FutureTask对Callable进行封装,最终通过Future获取结果(下面的实例会进一步说明)
(补充说明:Callable其实也是Executor框架的一部分,详情可参考:JUC----线程池(二)里的第三点)
2. Future
Future就是对具体的Runnable或者Callable任务的执行结果进行取消,查询是否完成,获取结果。比较时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
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接口下提供了五个方法
- cancel( ): 该方法用来取消任务,如果取消任务成功返回true, 如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务。
任务还没有开始,mayInterruptIfRunning无论设置为true还是false, 它的返回值都为false (因为任务还没执行)
任务已结束,mayInterruptIfRunning无论设置为true还是false, 它的返回值都为false (因为任务已完成)
任务已经启动,mayInterruptIfRunning设置为true,表示中断正在运行的任务,中断成功则返回true; 若mayInterruptIfRunning设置为false表示不会去中断已经在运行的任务,返回false.
- isCancelled( ): 表示任务是否被取消成功 ,取消成功返回true
- isDone(): 表示任务是否已经完成,若完成则返回true.
- get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
- get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
从上面的方法可以知道Future提供了三种功能:
1) 判断任务是否完成
2) 能够中断任务
3) 能够获取执行结果
但由于Future只是一个接口,为了使用它里面的功能,我们必须使用它的间接继承类 FutureTask。
3. FutureTask
它实现了了RunnableFuture接口,而RunnableFuture接口又继承自Runnable和Future接口,所以FutureTask既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
相关构造器:
public FutureTask(Callable<V> callable) {
}
public FutureTask(Runnable runnable, V result) {
}
事实上,FutureTask是Future接口的一个唯一实现类。
**使用场景上:**当前的计算任务相对比较耗时,但在未来的某一刻,还是需要用到该计算结果的时候,我们可以使用Future, FutureTask。
例如:对于一个文档里面包含有多种类型的文件时,图片(云端)获取时间相对较长;文字(本地)获取时间相对较短;我们这里可以开启多个线程去拿资源,然后在最终资源收集上使用FutureTask.get() 去阻塞获取我们图片的资源。
4. 使用实例
我们这里开启一个线程去执行计算任务,然后在主线程通过一个FutureTask.get() 以阻塞的方式获取计算结果。(这里可以通过随机值来表示获取结果或者取消任务)
package 并发工具类.future;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/*Future的测试*/
public class UseFuture {
/*实现Callable接口,允许有返回值*/
private static class UseCallable implements Callable<Integer>{
private int sum;
@Override
public Integer call() throws Exception {
System.out.println("Callable子线程开始计算");
Thread.sleep(2000);
for (int i = 0; i < 5000; i++) {
sum = sum + i;
}
System.out.println("Callable子线程计算完成,结果="+sum);
return sum;
}
}
public static void main(String[] args) throws Exception {
UseCallable useCallable = new UseCallable();
FutureTask<Integer> futureTask = new FutureTask<>(useCallable);
new Thread(futureTask).start();
Random r = new Random();
Thread.sleep(1000);
if (r.nextBoolean()){ //随机决定是获得结果还是停止任务
System.out.println("Get UseCallable result = "+ futureTask.get());
}else{
System.out.println("中断计算");
futureTask.cancel(true);
}
}
}
当r.nextBoolean() 取得true时,表示获取结果值
当r.nextBoolean() 取得false时,表示取消任务