基于 Java 的简单多线程并发任务调度系统

完整代码

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 是一个非常有用的工具,尤其在处理大量异步任务时,可以简化代码逻辑,提高程序的性能和可维护性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萧十一郎@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值