我们不生产代码,我们只是代码的搬运工。。。
在springboot中,只需如下两步实现多线程:
1.自定义线程池的配置类,并在类上添加@EnableAsync 注解,
package com.gxcards.common.executor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
/**
* @author cq
* @date 2019/11/12
* @descrption 线程池初始化
* version 1.0
*/
@Configuration
@EnableAsync
public class ExecutorConfig {
private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);
@Bean
public Executor asyncServiceExecutor() {
logger.info("start asyncServiceExecutor");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(10);
//配置最大线程数
executor.setMaxPoolSize(20);
executor.setKeepAliveSeconds(60);
//配置队列大小
executor.setQueueCapacity(99999);
//配置线程池中的线程的名称前缀
executor.setThreadNamePrefix("async-service-");
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
//执行初始化
executor.initialize();
return executor;
}
}
2.然后在需要异步的方法上使用@Async(“线程池名称”) 该方法就可以异步执行了。
/**
* 记录用户登录历史,添加@Async注解异步执行
* @param memberLoginHis
* @return
*/
@Async
public void save(MemberLoginHis memberLoginHis) {
memberLoginHis.setCreateTime(new Date());
memberLoginHis.setType("WEB");
// 获取IP地址对应的城市
memberLoginHis.setAddress(AddressUtils.getAddresses(memberLoginHis.getIp()));
emberLoginHisDao.save(memberLoginHis);
}
PS:异步线程没生效的原因
1.没有添加注解@EnableAsync注解。//很明显排除
2.异步方法使用注解@Async的返回值只能为void或者Future。 // 也排除
3.没有走Spring的代理类。因为@Transactional和@Async注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的。那么注解失效的原因就很明显了,有可能因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器
PS解决方法:
1和2没啥好说的,做相应修改就OK,
这里具体说一下第三种情况的解决方法。
1.注解的方法必须是public方法。
2.方法一定要从另一个类中调用,也就是从类的外部调用,类的内部调用是无效的。
3.如果需要从类的内部调用,需要先获取其代理类,下面上代码
BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
MemberService memberService = (MemberService) factory.getBean("memberService");
memberService.XXXX();//异步方法