目录
1.Future 的作用
2.Callable 和 Future 的关系
3.Future 的方法和用法
1.get() 方法:获取结果
2.isDone() 方法:判断是否执行完毕
3.cancel 方法:取消任务的执行
4.isCancelled() 方法:判断是否被取消
4.用 FutureTask 来创建 Future
Future 最主要的作用是,比如当做一定运算的时候,运算过程可能比较耗时,有时会去查数据库,或是繁重的计算,比如压缩、加密等,在这种情况下,如果我们一直在原地等待方法返回,显然是不明智的,整体程序的运行效率会大大降低。我们可以把运算的过程放到子线程去执行,再通过 Future 去控制子线程执行的计算过程,最后获取到计算结果。这样一来就可以把整个程序的运行效率提高,是一种异步的思想。
Callable 接口相比于 Runnable 的一大优势是可以有返回结果。
可以用 Future 类的 get 方法来获取 。因此,Future 相当于一个存储器,它存储了 Callable 的 call 方法的任务结果。
除此之外,我们还可以通过 Future 的 isDone 方法来判断任务是否已经执行完毕了,还可以通过 cancel 方法取消这个任务,或限时获取任务的结果等。
Future 接口的代码,一共有 5 个方法。
public interface Future {boolean cancel(boolean mayInterruptIfRunning);boolean isCancelled();boolean isDone();V get() throws InterruptedException, ExecutionException;V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutExceptio
}
代码样例:
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.*;/*** @author cf* @description: FutureDemo* @date 2023/3/17下午 2:46*/
public class FutureDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService service = Executors.newFixedThreadPool(10);Map params = new HashMap<>();params.put("name",1111);Future future = service.submit(new CallableTask(params));System.out.println(future.get());service.shutdown();}static class CallableTask implements Callable {private Map params;public CallableTask(Map params) {this.params = params;}@Overridepublic Integer call() throws Exception {System.out.println("参数:"+params.get("name"));Thread.sleep(3000);return new Random().nextInt();}}
}
该方法是用来判断当前这个任务是否执行完毕了。
这个方法如果返回 true 则代表执行完成了;如果返回 false 则代表还没完成。但这里如果返回 true,并不代表这个任务是成功执行的,比如说任务执行到一半抛出了异常。那么在这种情况下,对于这个 isDone 方法而言,它其实也是会返回 true 的,因为对它来说,虽然有异常发生了,但是这个任务在未来也不会再被执行,它确实已经执行完毕了。所以 isDone 方法在返回 true 的时候,不代表这个任务是成功执行的,只代表它执行完毕了。
案例:
package thread;import java.util.concurrent.*;/*** @author cf* @description: FutureException* @date 2023/3/17下午 2:59*/
public class FutureException {public static void main(String[] args) {ExecutorService service = Executors.newFixedThreadPool(20);Future future = service.submit(new CallableTask());try {for (int i = 0; i < 5; i++) {System.out.println(i);Thread.sleep(500);}System.out.println(future.isDone());future.get();} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}static class CallableTask implements Callable {@Overridepublic Integer call() throws Exception {throw new IllegalArgumentException("Callable抛出异常");}}
}
结果:
0
1
2
3
4
true
java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: Callable抛出异常at java.util.concurrent.FutureTask.report(FutureTask.java:122)at java.util.concurrent.FutureTask.get(FutureTask.java:192)at thread.FutureException.main(FutureException.java:21)
Caused by: java.lang.IllegalArgumentException: Callable抛出异常at thread.FutureException$CallableTask.call(FutureException.java:34)at thread.FutureException$CallableTask.call(FutureException.java:30)at java.util.concurrent.FutureTask.run(FutureTask.java:266)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)at java.lang.Thread.run(Thread.java:748)
这段代码证明了三件事情:
如果不想执行某个任务了,则可以使用 cancel 方法,会有以下三种情况:
第三种那么如何选择传入 true 还是 false 呢?
传入 false 适用于什么情况呢?
我们不知道这个任务是否支持取消(是否能响应中断),因为在大多数情况下代码是多人协作的,对于这个任务是否支持中断,我们不一定有十足的把握,那么在这种情况下也应该传入 false。
如果这个任务一旦开始运行,我们就希望它完全的执行完毕。在这种情况下,也应该传入 false。
这就是传入 true 和 false 的不同含义和选择方法。
最后一个方法是 isCancelled 方法,判断是否被取消,它和 cancel 方法配合使用,比较简单。
除了用线程池的 submit 方法会返回一个 future 对象之外,同样还可以用 FutureTask 来获取 Future 类和任务的结果。
FutureTask 首先是一个任务(Task),然后具有 Future 接口的语义,因为它可以在将来(Future)得到执行的结果。
FutureTask 的代码实现:
public class FutureTask implements RunnableFuture{...
}
可以看到,它实现了一个接口,这个接口叫作 RunnableFuture。我们再来看一下 RunnableFuture 接口的代码实现:
public interface RunnableFuture extends Runnable, Future {void run();
}
它们的关系如下图所示:

既然 RunnableFuture 继承了 Runnable 接口和 Future 接口,而 FutureTask 又实现了 RunnableFuture 接口,所以 FutureTask 既可以作为 Runnable 被线程执行,又可以作为 Future 得到 Callable 的返回值。
典型用法是,把 Callable 实例当作 FutureTask 构造函数的参数,生成 FutureTask 的对象,然后把这个对象当作一个 Runnable 对象,放到线程池中或另起线程去执行,最后还可以通过 FutureTask 获取任务执行的结果。
案例:
package thread;import java.util.concurrent.*;/*** @author cf* @description: FutureTaskDemo* @date 2023/3/17下午 3:41*/
public class FutureTaskDemo {public static void main(String[] args) {Task task = new Task();FutureTask integerFutureTask = new FutureTask<>(task);ExecutorService service = Executors.newFixedThreadPool(20);service.submit(integerFutureTask);try {System.out.println("task运行结果:" + integerFutureTask.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}
}class Task implements Callable {@Overridepublic Integer call() throws Exception {System.out.println("子线程正在计算");int sum = 0;for (int i = 0; i < 100; i++) {sum += i;}return sum;}
}
代码首先创建了一个实现了 Callable 接口的 Task,然后把这个 Task 实例传入到 FutureTask 的构造函数中去,创建了一个 FutureTask 实例,并且把这个实例当作一个 Runnable 放到 线程池中去执行,最后再用 FutureTask 的 get 得到结果,并打印出来。
下一篇:基础IO【Linux】