面试题答案
一键面试1. Executor接口和ExecutorService接口的主要区别
- Executor接口:
- 是一个非常基础的接口,它只定义了一个方法
execute(Runnable task)
,用于提交一个Runnable
任务到线程池中执行。它不提供任务执行结果的获取,也没有对线程池生命周期管理的方法。主要目的是将任务的提交和执行分离,简化任务执行的管理。
- 是一个非常基础的接口,它只定义了一个方法
- ExecutorService接口:
- 继承自
Executor
接口,扩展了许多功能。它不仅可以提交Runnable
任务,还可以提交Callable
任务(Callable
任务有返回值,而Runnable
任务无返回值)。 - 提供了获取任务执行结果的方法,例如通过
Future
对象来获取Callable
任务的执行结果。 - 具备管理线程池生命周期的方法,如
shutdown()
用于平滑关闭线程池,shutdownNow()
用于立即关闭线程池等。
- 继承自
2. 选择ExecutorService的原因
在需要提交任务并获取任务执行结果,同时管理线程池生命周期的场景中,Executor
接口无法满足获取任务执行结果和管理线程池生命周期的需求。而 ExecutorService
接口集成了这些功能,所以更适合该场景。
3. 代码示例
import java.util.concurrent.*;
public class ExecutorServiceExample {
public static void main(String[] args) {
// 创建一个固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
// 提交Callable任务
Future<Integer> future = executorService.submit(() -> {
// 模拟任务执行
Thread.sleep(2000);
return 42;
});
try {
// 获取任务执行结果,这里会阻塞直到任务完成
Integer result = future.get();
System.out.println("任务执行结果: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
// 关闭线程池
executorService.shutdown();
}
}
}
在上述代码中:
- 通过
Executors.newFixedThreadPool(2)
创建了一个固定大小为2的线程池。 - 使用
executorService.submit(Callable<T> task)
提交了一个Callable
任务,该任务返回一个Future
对象。 - 通过
future.get()
获取任务的执行结果,此操作会阻塞当前线程直到任务完成。 - 最后通过
executorService.shutdown()
平滑关闭线程池。