当前位置:首页 > 技术 > 正文内容

Spring Boot 中基于线程池的异步任务处理

访客 技术 2026年6月21日 1

在 Spring Boot 应用中,可以通过 @EnableAsync@Async 注解实现异步任务的多线程执行。这种方式能够有效提升系统响应能力,尤其适用于耗时操作如文件处理、远程调用等场景。

启用异步支持的基本步骤

  1. 在配置类上添加 @EnableAsync 启用异步功能。
  2. 定义一个或多个自定义线程池,推荐优于使用默认线程池。
  3. 在目标方法上标注 @Async,确保该方法为 public 且由外部类调用。

单一任务类型与共享线程池

当应用中仅存在一种异步任务时,可配置一个通用线程池供所有异步方法共用。

@Configuration
@EnableAsync
public class TaskExecutionConfig {

    @Bean("sharedExecutor")
    public Executor buildSharedPool() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(30);
        executor.setQueueCapacity(200);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("Worker-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        executor.initialize();
        return executor;
    }
}

服务类中声明异步方法:

@Service
public class BackgroundTaskService {

    private static final Logger log = LoggerFactory.getLogger(BackgroundTaskService.class);

    @Async("sharedExecutor")
    public void executeTask(int taskId) throws InterruptedException {
        log.info("任务 {} 开始执行", taskId);
        Thread.sleep(8000); // 模拟耗时操作
        log.info("任务 {} 执行完成", taskId);
    }
}

控制器触发批量任务:

@RestController
@RequestMapping("/jobs")
public class JobController {

    @Autowired
    private BackgroundTaskService taskService;

    @GetMapping("/run")
    public ResponseEntity<String> startJobs() {
        for (int i = 1; i <= 15; i++) {
            taskService.executeTask(i);
        }
        return ResponseEntity.ok("任务已提交");
    }
}

此时,15 个任务将由线程池统一调度:前 10 个直接分配线程,后 5 个进入队列等待空闲线程释放。

多类型任务隔离执行

若系统包含不同性质的任务(如 I/O 密集型和 CPU 密集型),建议分配独立线程池以避免资源争抢。

@Configuration
@EnableAsync
public class DedicatedPoolConfig {

    @Bean("ioPool")
    public Executor buildIoBoundPool() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(8);
        executor.setMaxPoolSize(40);
        executor.setQueueCapacity(150);
        executor.setThreadNamePrefix("IoWorker-");
        executor.setKeepAliveSeconds(120);
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }

    @Bean("cpuPool")
    public Executor buildCpuBoundPool() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(4);
        executor.setMaxPoolSize(8);
        executor.setQueueCapacity(50);
        executor.setThreadNamePrefix("CpuWorker-");
        executor.setKeepAliveSeconds(60);
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        executor.initialize();
        return executor;
    }
}

任务服务分别绑定不同线程池:

@Service
public class SpecializedTaskService {

    private static final Logger log = LoggerFactory.getLogger(SpecializedTaskService.class);

    @Async("ioPool")
    public void handleFileUpload(int jobId) throws InterruptedException {
        log.info("I/O 任务 {} 启动", jobId);
        Thread.sleep(12000);
        log.info("I/O 任务 {} 完成", jobId);
    }

    @Async("cpuPool")
    public void processData(int jobId) throws InterruptedException {
        log.info("计算任务 {} 启动", jobId);
        Thread.sleep(7000);
        log.info("计算任务 {} 完成", jobId);
    }
}

控制器同时触发两类任务:

@RestController
@RequestMapping("/tasks")
public class TaskController {

    @Autowired
    private SpecializedTaskService service;

    @GetMapping("/launch")
    public String launchMixedTasks() {
        for (int i = 1; i <= 6; i++) {
            service.handleFileUpload(i);
            service.processData(i);
        }
        return "混合任务已提交";
    }
}

访问接口后,日志显示两类任务按各自线程池策略并行执行,互不影响。

关键注意事项

  • 调用隔离:@Async 方法必须被外部 Bean 调用,同一类内直接调用不会触发代理机制,导致异步失效。
  • 返回值限制:异步方法若需返回结果,应使用 Future<T>CompletableFuture<T> 包装。
  • 异常处理:未捕获的异常可能导致任务中断,建议在方法内部进行 try-catch 处理。

线程池参数说明与执行逻辑

参数说明
corePoolSize核心线程数,常驻内存的最小工作线程数量
maxPoolSize最大线程上限,超出队列容量后允许创建的额外线程数
queueCapacity待处理任务缓冲区大小
keepAliveSeconds非核心线程空闲存活时间
rejectedExecutionHandler拒绝策略,常见有 AbortPolicy(抛异常)、CallerRunsPolicy(由调用者线程执行)等

任务调度顺序:新任务优先分配给空闲核心线程 → 若无空闲核心线程则入队 → 队列满时启动非核心线程 → 达到最大线程数且队列满,则触发拒绝策略。

例如:核心线程=2,最大线程=4,队列容量=5。
- ≤2 个任务:仅使用核心线程
- 3~7 个任务:2 核心 + (1~5) 入队
- 8~9 个任务:2 核心 + 5 队列 + (1~2) 新建线程
- ≥10 个任务:超出容量,执行拒绝策略

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。