FutureTask用来解决什么问题的? 为什么会出现?
FutureTask为Future接口提供了基础实现,可以用来获取异步任务的返回结果,或者取消异步任务的执行等。
FutureTask如何用?
- 定义Task类,继承Callable接口,实现call方法
- 新建FutureTask实例,将步骤1中的Task类实例作为构造函数参数传入
- 新建Thread对象,将步骤2中的FutureTask实例作为构造函数参数传入
- 启动Thread,异步执行任务
- 通过FutureTask实例的get、cancel等方法来获取结果或取消执行
示例代码如下:
package org.example.app;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class CallDemo {
// 1. 继承Callable接口,实现call()方法,泛型参数为要返回的类型
static class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("Thread [" + Thread.currentThread().getName() + "] is running");
int result = 0;
for(int i = 0; i < 100;++i) {
result += i;
}
Thread.sleep(3000);
return result;
}
}
public static void main(String[] args) {
/**
* 第一种方式:Future + ExecutorService
* Task task = new Task();
* ExecutorService service = Executors.newCachedThreadPool();
* Future<Integer> future = service.submit(task1);
* service.shutdown();
*/
/**
* 第二种方式: FutureTask + ExecutorService
* ExecutorService executor = Executors.newCachedThreadPool();
* Task task = new Task();
* FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
* executor.submit(futureTask);
* executor.shutdown();
*/
/**
* 第三种方式:FutureTask + Thread
*/
// 2. 新建FutureTask,需要一个实现了Callable接口的类的实例作为构造函数参数
FutureTask<Integer> futureTask = new FutureTask<Integer>(new Task());
// 3. 新建Thread对象并启动
Thread thread = new Thread(futureTask);
thread.setName("Task thread");
thread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread [" + Thread.currentThread().getName() + "] is running");
// 4. 调用isDone()判断任务是否结束
if(!futureTask.isDone()) {
System.out.println("Task is not done");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int result = 0;
try {
// 5. 调用get()方法获取任务结果,如果任务没有执行完成则阻塞等待
result = futureTask.get();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("result is " + result);
}
}
FutureTask类关系

FutureTask相关类图.png
实现原理

FutureTask相关时序图.png
- 调用get()方法时,会将当前线程加入到FutureTask的等待队列中,并调用LockSupport.park()方法对其进行阻塞。
- 调用run()方法时,任务执行完成后,将结果赋值给outcome,然后调用LockSupport.unpark()方法唤醒等待队列中阻塞的线程,然后清空等待队列。
- 当阻塞的线程被唤醒后,会将outcome作为返回值进行返回。
- 带有超时机制的get()方法,是通过调用LockSupport.parkNanos()方法实现超时等待的。