本系列内容是我自己对于自己做项目过程中的问题的记录,所以希望各位看官看到有误的地方帮忙指正。本系列最主要是想说明一下我在使用tomcat工作线程池executor中的多个线程做一些事情的时候遇到的一些问题。
在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; } }