关于配置执行定时任务和异步任务的线程池配置类
关于配置执行定时任务和异步任务的线程池配置类1
package com.liftsail.rsademo.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.config.Task;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @Author 张帆
* @Description 不积跬步无以至千里
* @Date 2022/11/23 20:50
*/
@Configuration
@Slf4j
public class ThreadPoolConfig implements AsyncConfigurer, SchedulingConfigurer {
/**
* 异步任务执行线程池参数
*/
private static final Integer CORE_POOL_SIZE = 5;
private static final Integer MAX_POOL_SIZE = 200;
private static final Integer QUEUE_CAPACITY = 2000;
private static final String THREAD_NAME_PREFIX = "async-thread-";
private static final Integer KEEP_ALIVE_SECONDS = 60;
/**
* 定时任务线程池线程名前缀
*/
private static final String SCHEDULER_THEREAD_NAME_PREFIX = "task-";
/**
* @param
* @description: 创建执行spring task定时任务的线程池,调用@scheduled注解的定时任务
* @author: xiaomifeng1010
* @date: 2022/3/13
* @return: TaskScheduler
**/
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(10);
threadPoolTaskScheduler.setThreadNamePrefix(SCHEDULER_THEREAD_NAME_PREFIX);
threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
return threadPoolTaskScheduler;
}
/**
* @param
* @description: 创建执行异步任务的线程池,用于调用 @async注解的方法
* @author: xiaomifeng1010
* @date: 2022/3/13
* @return: ThreadPoolTaskExecutor
**/
@Bean("asyncThreadPoolTaskExecutor")
public ThreadPoolTaskExecutor asyncThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
// 核心线程数量
threadPoolTaskExecutor.setCorePoolSize(CORE_POOL_SIZE);
// 最大线程数量
threadPoolTaskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
// 队列中最大任务数
threadPoolTaskExecutor.setQueueCapacity(QUEUE_CAPACITY);
// 线程名称前缀
threadPoolTaskExecutor.setThreadNamePrefix(THREAD_NAME_PREFIX);
// 当达到最大线程数时如何处理新任务(拒绝策略)
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 线程空闲后最大存活时间
threadPoolTaskExecutor.setKeepAliveSeconds(KEEP_ALIVE_SECONDS);
// 初始化线程池
threadPoolTaskExecutor.initialize();
// 关闭线程池
threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
return threadPoolTaskExecutor;
}
/**
* Callback allowing a {@link TaskScheduler
* TaskScheduler} and specific {@link Task Task}
* instances to be registered against the given the {@link ScheduledTaskRegistrar}.
*
* @param taskRegistrar the registrar to be configured.
*/
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setTaskScheduler(threadPoolTaskScheduler());
}
/**
* The {@link Executor} instance to be used when processing async
* method invocations.
*/
@Override
public Executor getAsyncExecutor() {
return asyncThreadPoolTaskExecutor();
}
/**
* The {@link AsyncUncaughtExceptionHandler} instance to be used
* when an exception is thrown during an asynchronous method execution
* with {@code void} return type.
*/
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (throwable, method, objects) -> {
log.error("异步任务执行出现异常, message {}, method {}, params {}", throwable, method, objects);
};
}
}
@Scheduled和@Async在spring-boot中共享相同的线程池
我已经配置了两个不同的线程池,一个用于@Scheduled和另一个用于@Async.但是,我注意到@Async没有使用线程池.
这是Scheduler配置
@Configuration
@EnableScheduling
public class SchedulerConfig implements SchedulingConfigurer {
private final int POOL_SIZE = 10;
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(POOL_SIZE);
threadPoolTaskScheduler.setThreadNamePrefix("my-sched-pool-");
threadPoolTaskScheduler.initialize();
scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
}
}
这是Async的配置
@Configuration
@EnableAsync
public class AppConfig {
@Bean(name = "asyncTaskExecutor")
public TaskExecutor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(15);
executor.setMaxPoolSize(15);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("my-async-pool-");
executor.initialize();
return executor;
}
}
这是我如何调用它们
@Scheduled(fixedRateString = "2000" )
public void schedule() {
log.debug("here is the message from schedule");
asyncMethod();
}
@Async("asyncTaskExecutor")
public void asyncMethod(){
log.info("here is the message from async");
}
这是日志
{"thread":"my-sched-pool-1","level":"DEBUG","description":"here is the message from schedule"}
{"thread":"my-sched-pool-1","level":"INFO","description":"here is the message from async"}
您可以注意到,两个日志都具有该调度程序的相同池.但我希望看到第二个来自异步.
如果你声明它们被声明的@Async方法,class你实际上绕过了Spring的代理机制,这就是为什么你的例子不起作用的原因.尝试从单独class注释的方法@Service或任何其他@Component类型调用该方法.
@Service
SomeScheduledClass {
private final SomeAsyncClass someAsyncClass;
public SomeScheduledClass(SomeAsyncClass someAsyncClass) {
this.someAsyncClass = someAsyncClass;
}
@Scheduled(fixedRateString = "2000" )
public void schedule() {
log.debug("here is the message from schedule");
someAsyncClass.asyncMethod();
}
}
@Service
SomeAsyncClass {
@Async("asyncTaskExecutor")
public void asyncMethod(){
log.info("here is the message from async");
}
}
异步线程池与定时任务线程池
两个线程池都是一样的步骤:
第一步是线程池配置;
第二步写具体定时或异步任务。
先看异步线程池:
package com.xnpool.common.async;
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;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableAsync // 启用异步任务
public class AsyncConfiguration {
@Value("${asynctask.corepoolsize}")
private int corepoolsize;
@Value("${asynctask.maxpoolsize}")
private int maxpoolsize;
@Value("${asynctask.queuecapacity}")
private int queuecapacity;
@Value("${asynctask.keepaliveseconds}")
private int keepaliveseconds;
@Value("${asynctask.threadnameprefix}")
private String threadnameprefix;
@Value("${asynctask.waitfortaskstocompleteonshutdown}")
private boolean waitfortaskstocompleteonshutdown;
@Value("${asynctask.awaitterminationseconds}")
private int awaitterminationseconds;
// 声明一个线程池(并指定线程池的名字)
@Bean("taskExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程数5:线程池创建时候初始化的线程数
executor.setCorePoolSize(corepoolsize);
//最大线程数5:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
executor.setMaxPoolSize(maxpoolsize);
//缓冲队列500:用来缓冲执行任务的队列
executor.setQueueCapacity(queuecapacity);
//允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
executor.setKeepAliveSeconds(keepaliveseconds);
//线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
executor.setThreadNamePrefix(threadnameprefix);
/*
* 线程池对拒绝任务(无线程可用)的处理策略
* AbortPolicy:丢弃任务,直接抛出java.util.concurrent.RejectedExecutionException异常,默认的策略
* CallerRunsPolicy:这个策略重试添加当前的任务,他会自动重复调用 execute() 方法,直到成功
* DiscardOldestPolicy: 丢弃队列最前面的任务,然后重新尝试执行任务,会导致被丢弃的任务无法再次被执行
* DiscardPolicy:抛弃当前任务;会导致被丢弃的任务无法再次被执行,但是不抛出异常
*/
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setWaitForTasksToCompleteOnShutdown(waitfortaskstocompleteonshutdown);
executor.setAwaitTerminationSeconds(awaitterminationseconds);
executor.initialize();
return executor;
}
}
启用了线程异步任务后,就可以写代码了:
package com.xnpool.common.async;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xnpool.common.constant.AuthConstant;
import com.xnpool.common.mapper.ds1.CCoinMapper;
import com.xnpool.common.mapper.ds1.SubuserMapper;
import com.xnpool.common.mapper.ds2.UserAddressMapper;
import com.xnpool.common.model.CommonCoin;
import com.xnpool.common.model.Subuser;
import com.xnpool.common.model.UserAddress;
import com.xnpool.common.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@Service
public class AsyncTaskService {
@Autowired
private UserAddressMapper userAddressMapper;
@Autowired
private TableUtil tableUtil;
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private CCoinMapper coinMapper;
@Autowired
private SubuserMapper subuserMapper;
@Autowired
private RedisService redisService;
/**
*
* @param pid 企业id
* @param aid 应用id
* @param uid 用户id
* @param cid 子账号id
*/
@Async("taskExecutor")//指定了异步线程池名
public void executeAsyncTask(Long pid,Long aid, String uid, String cid,boolean b) {
String sql="select coin_id as coinId,coin_name as coinName,price,contract_coin_id as contractCoinId,contract_coin as contractCoin from t_coin";
/*//查询币种列表 new BeanPropertyRowMapper<CommonCoin>(CommonCoin.class)*/
//BeanPropertyRowMapper<CommonCoin> rowMapper = new BeanPropertyRowMapper<CommonCoin>(CommonCoin.class);
List<CommonCoin> coinIds =coinMapper.selectList(new QueryWrapper<CommonCoin>());
ArrayList<UserAddress> userAddressArrayList = new ArrayList<UserAddress>();
String existsAddress = tableUtil.isExistsAddress(pid);//一个企业一个用户币种关联表。(没有表就先创建)
log.info("增加用户或子账户的币种地址============");
if (existsAddress != null && userAddressArrayList.size()>0) {
userAddressMapper.insertCoinBase(existsAddress, userAddressArrayList);
}
}
@Async("taskExecutor")//指定了异步线程池名
public void executeAsyncCoinTask(Integer coinId, String coinName,Integer isContact, Integer contactCoinId) {
List<Subuser> ulist=subuserMapper.findAllSubList();
if(CollectionUtil.isNotEmpty(ulist)){
Map<Long,List<Subuser>> map=ulist.stream().filter(u->u.getEnterpriseId()!=null).collect(Collectors.groupingBy(Subuser::getEnterpriseId));
Long platId=getPalateFromId();
String existsAddress = tableUtil.isExistsAddress(pid);//一个企业一个用户币种关联表。(没有表就先创建)
log.info("增加用户或子账户的币种地址============");
if (existsAddress != null && userAddressArrayList.size()>0) {
userAddressMapper.insertCoinBase(existsAddress, userAddressArrayList);
}
}
}
}
}
下面贴定时任务线程池:
package com.xnpool.admin.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import java.util.concurrent.Executor;
@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskScheduleExecutor());
}
@Bean(destroyMethod="shutdown")
public Executor taskScheduleExecutor() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10);
scheduler.setThreadNamePrefix("schedule_task-");
scheduler.setAwaitTerminationSeconds(600); 线程池关闭前最大等待时间,确保最后一定关闭
scheduler.setWaitForTasksToCompleteOnShutdown(true);// 线程池关闭时等待所有任务完成
return scheduler;
//return Executors.newScheduledThreadPool(10); //指定线程池大小
}
}
具体使用定时任务的地方:
@Slf4j
@Service
public class SendMessageService {
@Autowired
private PushUtil pushUtil;
@Autowired
private TransactionMapper transactionsMapper;
@Autowired
private TUserMapper userMapper;
@Autowired
private TCoinMapper coinMapper;
@Autowired
private TNoticeMapper noticeMapper;
@Autowired
private TMessageMapper messageMapper;
@Autowired
private TableUtil tableUtil;
@Autowired
private RedisService redisService;
/**
* 推送公告(把公告推送给所有手机用户,推送后,不管用户是否接收,公告状态改为已发送状态)
*/
@Scheduled(fixedDelay = 1000 * 60 * 5)
public void sendNotice() {
List<TNotice> notices = noticeMapper.selectList(new QueryWrapper<TNotice>().lambda().eq(TNotice::getIsSend,0));
if (notices != null && notices.size() > 0) {
List<Integer> idList = notices.stream().map(TNotice::getId).collect(Collectors.toList());
List<TUser> userList = userMapper.selectClientList();
List<String> clinetList=userList.stream().map(TUser::getClientid).collect(Collectors.toList());
}
}
/**
* 推送消息(将所有状态是已完成未发送的交易,发送给用户。 发送完就把状态改成已发送。)
*/
@Scheduled(fixedDelay = 1000 * 10)
public void sendTx() {
}
}
这两个定时任务没有指定线程池名字,但已确认它是在线程池ScheduleConfig 中。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?