Springboot项目使用@Async异步指定自定义线程池

本文介绍了如何在Spring框架下自定义线程池配置,启用异步调用,并使用AOP解决同一类内方法调用的拦截问题。还提到使用CountDownLatch确保主线程与子线程同步的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

自定义线程池配置并开启异步

@Bean(name = "asyncServiceExecutor")

自定义bean name,之后的异步调用中需要与之对应

package com.yms.task;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

/**
 * @author yms
 * @description:
 * @date: 2023/10/20 9:38
 */
@Configuration
@EnableAsync
@Slf4j
public class ExecutorConfig {
  @Value("${async.executor.thread.core_pool_size:8}")
  private int corePoolSize;
  @Value("${async.executor.thread.max_pool_size:16}")
  private int maxPoolSize;
  @Value("${async.executor.thread.queue_capacity:5}")
  private int queueCapacity;
  @Value("${async.executor.thread.name.prefix:async-transfer-data-}")
  private String namePrefix;

  @Bean(name = "asyncServiceExecutor")
  public Executor asyncServiceExecutor() {
    log.warn("start asyncServiceExecutor");
    //在这里修改
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    //配置核心线程数
    executor.setCorePoolSize(corePoolSize);
    //配置最大线程数
    executor.setMaxPoolSize(maxPoolSize);
    //配置队列大小
    executor.setQueueCapacity(queueCapacity);
    //配置线程池中的线程的名称前缀
    executor.setThreadNamePrefix(namePrefix);
    // rejection-policy:当pool已经达到max size的时候,如何处理新任务
    // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    //执行初始化
    executor.initialize();
    return executor;
  }
}

异步调用

@Async("asyncServiceExecutor")

注解的属性值需要和配置类中的bean name保持一致

同一个类里面调用异步方法不生效:原因默认类内的方法调用不会被aop拦截,即调用方和被调用方是在同一个类中,是无法产生切面的,该对象没有被Spring容器管理。即@Async方法不生效。

解决办法:如果要使同一个类中的方法之间调用也被拦截,需要使用spring容器中的实例对象,而不是使用默认的this,因为通过bean实例的调用才会被spring的aop拦截

例如:AsyncService asyncService = context.getBean(AsyncService.class);

然后使用这个引用调用本地的方法即可达到被拦截的目的

备注:这种方法只能拦截protected,default,public方法,private方法无法拦截。这个是spring aop的一个机制。

package com.yms.task;

import com.yms.utils.sql.BatchDmlUtil;
import com.yms.dto.SysUserDto;
import com.yms.mapper.SysUserMapper;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

/**
 * @author yms
 * @description:
 * @date: 2023/10/20 9:41
 */
@Service
@Slf4j
public class AsyncUserService {
  @Async("asyncServiceExecutor")
  public void executeAsync(List<SysUserDto> dataList,CountDownLatch countDownLatch) {
    try{
      log.info("start user executeAsync");
      //异步线程要做的事情
      BatchDmlUtil.batchOperate(dataList,SysUserMapper.class,SysUserMapper::insert);
      log.info("end user executeAsync");
    }finally {
      // 很关键, 无论上面程序是否异常必须执行countDown,否则await无法释放
      countDownLatch.countDown();
    }
  }
}

业务代码

使用多线程栅栏CountDownLatch保证主线程与子线程的同步

    CountDownLatch latch = new CountDownLatch(transferList.size());
    for (List<SysUseDto> userList : transferList) {
      asyncUserService.executeAsync(userList, latch);
    }
    try {
      latch.await();
    } catch (InterruptedException e) {
      log.error("线程池异常阻塞:" + e.getMessage());
      e.printStackTrace();
    }

### Java `@Async` 注解自定义线程池配置及使用方法 #### 配置自定义线程池 为了使 `@Async` 使用特定的线程池而非 Spring 默认提供者,需先创建并配置一个 `TaskExecutor` Bean。这可以通过在任意 `@Configuration` 类中声明来完成。 ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.Executor; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration public class AsyncConfig { @Bean(name = "myExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(25); executor.initialize(); return executor; } } ``` 上述代码片段展示了如何通过设置核心线程数、最大线程数以及队列容量来自定义线程池属性[^1]。 #### 应用 `@Async` 注解 一旦有了自定义线程池,在目标服务类的方法上应用 `@Async` 注解即可让其异步运行。需要注意的是,这里指定了要使用线程池名称 `"myExecutor"` 来确保该方法由之前定义的任务执行器处理。 ```java import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class AsyncTest { @Async("myExecutor") public void executeAsync() { System.out.println(Thread.currentThread().getName()); System.out.println("Executing asynchronously"); } } ``` 当调用带有 `@Async` 的方法时,它将在不同的线程上下文中被执行,并不会阻塞主线程直到操作完成。 此外,为了让应用程序识别这些配置,还需启用全局异步支持: ```java import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableAsync public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 这样就完成了整个流程——从配置到实际运用 `@Async` 和自定义线程池的工作方式[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值