完整代码
import java.util.concurrent.*;
// 自定义任务类,实现 Callable 接口
class CustomTask implements Callable<String> {
private final int taskId;
public CustomTask(int taskId) {
this.taskId = taskId;
}
@Override
public String call() throws Exception {
System.out.println("任务 " + taskId + " 开始执行,当前线程: " + Thread.currentThread().getName());
// 模拟任务执行耗时
Thread.sleep(2000);
System.out.println("任务 " + taskId + " 执行完成");
return "任务 " + taskId + " 执行结果";
}
}
// 任务调度器类
public class TaskScheduler {
public static void main(String[] args) {
// 创建一个固定大小的线程池,包含 3 个线程
ExecutorService executorService = Executors.newFixedThreadPool(3);
// 创建一个任务列表
ExecutorCompletionService<String> completionService = new ExecutorCompletionService<>(executorService);
// 提交 5 个任务到任务调度系统
for (int i = 1; i <= 5; i++) {
completionService.submit(new CustomTask(i));
}
// 获取并处理任务结果
for (int i = 0; i < 5; i++) {
try {
Future<String> future = completionService.take();
String result = future.get();
System.out.println("获取到任务结果: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
// 关闭线程池
executorService.shutdown();
}
}
代码解释
CustomTask
类:- 实现了
Callable<String>
接口,这意味着该任务在执行完毕后会返回一个String
类型的结果。 call
方法是任务的核心逻辑,打印任务开始执行的信息,模拟 2 秒的任务执行耗时,然后打印任务执行完成的信息,最后返回任务执行结果。
- 实现了
TaskScheduler
类:main
方法是程序的入口点。- 使用
Executors.newFixedThreadPool(3)
创建一个固定大小为 3 的线程池,用于执行任务。 - 创建
ExecutorCompletionService<String>
对象,它可以将任务的提交和结果的获取解耦,方便我们按任务完成的顺序获取结果。 - 使用
for
循环提交 5 个CustomTask
任务到completionService
中。 - 再次使用
for
循环,通过completionService.take()
方法获取已经完成的任务的Future
对象,然后使用future.get()
方法获取任务的执行结果并打印。 - 最后调用
executorService.shutdown()
方法关闭线程池。
改进策略
- 异常处理增强:当前代码只是简单地打印异常堆栈信息,可以提供更详细的异常处理逻辑,例如记录日志、重试机制等。
- 动态调整线程池大小:使用固定大小的线程池在某些情况下可能不够灵活,可以根据任务的数量和系统资源动态调整线程池的大小。
- 任务优先级管理:可以为不同的任务设置优先级,让高优先级的任务先执行。
改进后的代码
import java.util.concurrent.*;
import java.util.logging.*;
// 自定义任务类,实现 Callable 接口
class CustomTask implements Callable<String> {
private final int taskId;
private final int priority;
private static final Logger LOGGER = Logger.getLogger(CustomTask.class.getName());
public CustomTask(int taskId, int priority) {
this.taskId = taskId;
this.priority = priority;
}
public int getPriority() {
return priority;
}
@Override
public String call() throws Exception {
LOGGER.info("任务 " + taskId + " 开始执行,当前线程: " + Thread.currentThread().getName());
// 模拟任务执行耗时
Thread.sleep(2000);
LOGGER.info("任务 " + taskId + " 执行完成");
return "任务 " + taskId + " 执行结果";
}
}
// 自定义优先级阻塞队列
class PriorityBlockingQueueWithCallable<T extends Callable<?>> extends PriorityBlockingQueue<T> {
public PriorityBlockingQueueWithCallable() {
super(11, (c1, c2) -> {
if (c1 instanceof CustomTask && c2 instanceof CustomTask) {
return ((CustomTask) c2).getPriority() - ((CustomTask) c1).getPriority();
}
return 0;
});
}
}
// 任务调度器类
public class TaskScheduler {
private static final Logger LOGGER = Logger.getLogger(TaskScheduler.class.getName());
public static void main(String[] args) {
// 创建一个可动态调整大小的线程池
ThreadPoolExecutor executorService = new ThreadPoolExecutor(
2, // 核心线程数
5, // 最大线程数
60, TimeUnit.SECONDS,
new PriorityBlockingQueueWithCallable<>()
);
ExecutorCompletionService<String> completionService = new ExecutorCompletionService<>(executorService);
// 提交 5 个任务到任务调度系统,设置不同的优先级
completionService.submit(new CustomTask(1, 3));
completionService.submit(new CustomTask(2, 1));
completionService.submit(new CustomTask(3, 2));
completionService.submit(new CustomTask(4, 5));
completionService.submit(new CustomTask(5, 4));
// 获取并处理任务结果
for (int i = 0; i < 5; i++) {
try {
Future<String> future = completionService.take();
String result = future.get();
LOGGER.info("获取到任务结果: " + result);
} catch (InterruptedException | ExecutionException e) {
LOGGER.severe("处理任务结果时出现异常: " + e.getMessage());
e.printStackTrace();
}
}
// 关闭线程池
executorService.shutdown();
}
}
Java 知识点讲解:ExecutorCompletionService
ExecutorCompletionService
是 Java 并发包 java.util.concurrent
中的一个实用类,用于管理异步任务的执行和结果获取。它结合了 Executor
和 BlockingQueue
的功能,主要有以下几个特点和优势:
- 任务结果获取顺序:与直接使用
ExecutorService
提交任务不同,ExecutorCompletionService
可以让我们按照任务完成的顺序获取结果,而不是按照任务提交的顺序。在上述代码中,通过completionService.take()
方法可以获取最先完成的任务的Future
对象,这样就可以及时处理已经完成的任务结果,提高程序的响应性。 - 解耦任务提交和结果获取:使用
ExecutorCompletionService
可以将任务的提交和结果的获取分离开来。我们可以先将多个任务提交到ExecutorCompletionService
中,然后在需要的时候从队列中获取已经完成的任务的结果,而不需要关心任务的执行顺序和时间。 - 异常处理方便:当任务执行过程中出现异常时,
ExecutorCompletionService
会将异常封装在Future
对象中,我们可以通过future.get()
方法捕获并处理这些异常,保证程序的健壮性。
总的来说,ExecutorCompletionService
是一个非常有用的工具,尤其在处理大量异步任务时,可以简化代码逻辑,提高程序的性能和可维护性。