自定义线程池配置并开启异步
@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();
}