Spring boot中使用tomcat多线程总结之controller
说明
本系列内容是我自己对于自己做项目过程中的问题的记录,所以希望各位看官看到有误的地方帮忙指正。本系列最主要是想说明一下我在使用tomcat工作线程池executor中的多个线程做一些事情的时候遇到的一些问题。
对于controller中的注意事项
在controller类中,我经常使用@Autowired注解来注入我需要用到的service类对象,一开始我以为只要在service类上使用@Scope(“prototype”)注解就可以保证每一个线程使用上这个service的不同的实例了,就例如这样。
//controller类
@Controller
@RequestMapping("/pipeline")
public class pipelineController {
@Autowired
private ProcessService processService;
@RequestMapping("exec")
@ResponseBody
public String executor(String key){
String execResult = processService.exeProcess(key);
return execResult;
}
}
//service类
@Service
@Scope("prototype")
public class ProcessService {
private String correlationDate = "";
public String exeProcess(String key){
correlationDate = correlationDate+key;
return correlationDate;
}
}
最后的结果显示,我还是太年轻,这样用会导致correlationDate值混乱。后来才想起来,@Scope(“prototype”)是可以保证每次注入都是一个新的实例对象,但是,我的controller实例是只有一个的,也就是说这里service只在创建controller实例的时候注入了一次,多个线程在使用controller的时候,并没有再去创建新的controller实例,而是就使用的springmvc容器里面的controller实例,所以就导致并没有去注入新的service实例,所以最后就导致多个线程同用一个service,导致correlationDate值混乱。
另外这里还提醒了我一点就是对于形参和方法内的局部变量,不需要为多线程安全问题去考虑会不会共用,因为这些变量都是存储在栈帧中的,栈帧是存放在线程的线程栈中的,线程栈是每个线程私有的,所以不存在多线程安全问题。
解决方案
为了解决这个@Autowird只注入一次的问题,这里可以在每次使用的时候才从springmvc容器中获取service实例。
//controller类
@Controller
@RequestMapping("/pipeline")
public class pipelineController {
@Autowired
private ApplicationContext applicationContext;
@RequestMapping("exec")
@ResponseBody
public String executor(String key){
ProcessService processService = applicationContext.getBean(ProcessService.class);
String execResult = processService.exeProcess(key);
return execResult;
}
}
这样既可解决controller中service在多线程中也只有一个实例的问题;
更高效的解决方案
如上每次都重新获取service实例是可以解决问题的,但是这引入另外一个问题,频繁的创建,销毁service对象增加了程序的负载,同时这减慢了请求的处理速度。为了实现每个线程单独使用自己的service实例,并且不要频繁的创建销毁service对象,可以使用ThreadLocal。具体实现如下:
//controller类
@Controller
@RequestMapping("/pipeline")
public class pipelineController {
public ThreadLocal<ProcessService> processServiceInstance = new ThreadLocal<>();
@Autowired
private ApplicationContext applicationContext;
@RequestMapping("exec")
@ResponseBody
public String executor(String key){
ProcessService processService = processServiceInstance.get();
if(processService == null){
processService = applicationContext.getBean(ProcessService.class);
processServiceInstance.set(processService)
}
String execResult = processService.exeProcess(key);
return execResult;
}
}