获取 Java 线程执行结果
在多线程入门,我们讲述了创建线程的 3 种方式,一种是直接继承 Thread,一种是实现 Runnable 接口,另外一种是实现 Callable 接口。
前 2 种方式都有一个缺陷:在执行完任务之后无法获取执行结果。 如果需要获取执行结果,就必须通过共享变量或者线程通信的方式来达到目的,这样使用起来就比较麻烦。
Java 1.5 提供了 Callable、Future、FutureTask,它们可以在任务执行完后得到执行结果,今天我们就来详细的了解一下。
无返回值的 Runnable
由于 Runnable 的 run() 方法的返回值为 void:
public interface Runnable {
public abstract void run();
}
所以在执行完任务之后无法返回任何结果。
有返回值的 Callable
Callable 位于 java.util.concurrent 包下,也是一个接口,它定义了一个 call() 方法:
public interface Callable<V> {
V call() throws Exception;
}
可以看到,call() 方法返回的类型是一个 V 类型的泛型。
一般会配合ExecutorService来使用Callable。------->executorService.submit(task);
异步计算结果 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;
}
一共声明了 5 个方法:
cancel()方法用来取消任务,如果取消任务成功则返回 true,如果取消任务失败则返回 false。参数 mayInterruptIfRunning 表示是否允许取消正在执行却没有执行完毕的任务,如果设置 true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论 mayInterruptIfRunning 为 true 还是 false,此方法肯定返回 false,即如果取消已经完成的任务会返回 false;如果任务正在执行,若 mayInterruptIfRunning 设置为 true,则返回 true,若 mayInterruptIfRunning 设置为 false,则返回 false;如果任务还没有执行,则无论 mayInterruptIfRunning 为 true 还是 false,肯定返回 true。isCancelled()方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。isDone()方法表示任务是否已经完成,若任务完成,则返回 true;get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回 null。
也就是说 Future 提供了三种功能:
- 1)判断任务是否完成;
- 2)能够中断任务;
- 3)能够获取任务执行结果。
由于 Future 只是一个接口,如果直接 new 的话,编译器是会有一个 ⚠️ 警告的,它会提醒我们最好使用 FutureTask。
FutureTask 是 Future 接口的一个唯一实现类,我们在前面的例子中 executorService.submit() 返回的就是 FutureTask
代码执行注意事项
- executorService.submit(task)开启多线程,task.call()主线程执行
- 下述代码的TaskScheduled执行步骤:
- 总共创建三个线程:(tasks[1]的sleep时间一致)
- 线程123执行任务123,主线程执行任务6
- 线程12执行任务45,主线程执行7
- 主线程执行8
- 总共创建三个线程:(tasks[1]的sleep时间一致)
// 创建一个固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
// 创建一系列 Callable
Callable<Integer>[] tasks = new Callable[5];
for (int i = 0; i < tasks.length; i++) {
final int index = i;
tasks[i] = () -> {
TimeUnit.SECONDS.sleep(index + 1);
nowDate();
return (index + 1) * 1000;
};
}
//按顺序为任务1-8
executorService.submit(tasks[1]);
executorService.submit(tasks[1]);
executorService.submit(tasks[1]);
executorService.submit(tasks[1]);
executorService.submit(tasks[1]);
tasks[1].call();
tasks[1].call();
tasks[1].call();