面试题答案
一键面试主要区别
- 概念本质:
- Future:是一个接口,定义了异步计算结果的获取等方法,比如
get()
获取结果,isDone()
判断任务是否完成等。它只是对异步计算结果的一种抽象表示。 - FutureTask:是一个类,它实现了
Future
接口和Runnable
接口。这意味着它既可以作为一个任务被线程执行,又可以作为异步计算的结果来获取。
- Future:是一个接口,定义了异步计算结果的获取等方法,比如
- 功能侧重:
- Future:侧重于提供获取异步操作结果的方式,以及检查异步操作状态的能力。它本身不包含执行任务的逻辑。
- FutureTask:除了具备
Future
获取结果等功能外,还封装了实际的任务执行逻辑。它可以被传递给ExecutorService
执行,或者直接由线程执行(因为实现了Runnable
接口)。
- 创建方式:
- Future:通常是通过
ExecutorService.submit()
方法返回,例如Future<Integer> future = executorService.submit(() -> { return 1 + 2; });
这里submit
方法会返回一个实现了Future
接口的对象。 - FutureTask:可以直接创建,例如
FutureTask<Integer> futureTask = new FutureTask<>(() -> { return 1 + 2; });
然后可以通过new Thread(futureTask).start();
来执行任务。
- Future:通常是通过
应用场景选择
- 使用Future的场景:
- 当你只关心异步操作的结果,并且该异步操作是通过
ExecutorService
提交任务时,使用Future
较为合适。例如,你有一个线程池ExecutorService
,你提交多个任务,只需要获取每个任务的最终结果,不关心任务执行的具体过程和细节,此时ExecutorService.submit()
返回的Future
就满足需求。 - 当你使用第三方库提供的异步操作,该库返回
Future
来表示异步结果时,你只能使用Future
来获取结果,例如一些网络异步请求库可能返回Future
类型的对象表示请求结果。
- 当你只关心异步操作的结果,并且该异步操作是通过
- 使用FutureTask的场景:
- 当你需要手动控制任务的执行,例如希望在主线程中通过
new Thread(futureTask).start();
这样的方式来启动任务,而不是依赖ExecutorService
时,FutureTask
是很好的选择。 - 当你需要将任务作为一个可复用的组件,既可以作为任务执行,又可以获取异步结果时,
FutureTask
更合适。比如你有一个复杂的计算任务,在不同的地方可能需要多次执行,同时又要获取执行结果,FutureTask
封装这个任务就很方便。
- 当你需要手动控制任务的执行,例如希望在主线程中通过