springmvc的controller是singleton的(非線程安全的),這也許就是他和struts2的區別吧
和Struts一樣,Spring的Controller默認是Singleton的,這意味着每個request過來,系統都會用原有的instance去處理,這樣導致了兩個結果:一是我們不用每次創建Controller,二是減少了對象創建和垃圾收集的時間;由於只有一個Controller的instance,當多個線程調用它的時候,它里面的instance變量就不是線程安全的了,會發生竄數據的問題。
當然大多數情況下,我們根本不需要考慮線程安全的問題,比如dao,service等,除非在bean中聲明了實例變量。因此,我們在使用spring mvc 的contrller時,應避免在controller中定義實例變量。
如:
public class Controller extends AbstractCommandController { ...... protected ModelAndView handle(HttpServletRequest request,HttpServletResponse response, Object command,BindException errors) throws Exception { company = ................; } protected Company company; }
在這里有聲明一個變量company,這里就存在並發線程安全的問題。
如果控制器是使用單例形式,且controller中有一個私有的變量a,所有請求到同一個controller時,使用的a變量是共用的,即若是某個請求中修改了這個變量a,則,在別的請求中能夠讀到這個修改的內容。。
有幾種解決方法:
1、在Controller中使用ThreadLocal變量
2、在spring配置文件Controller中聲明 scope="prototype",每次都創建新的controller
所在在使用spring開發web 時要注意,默認Controller、Dao、Service都是單例的。
【1】SpringMVC多線程環境中如何保證對象的安全性?
代碼如下:
@RequestMapping("/user") @Controller Class UserController { @Resource UserService userService; @RequestMapping("/add") public void testA(User user){ userService.add(user); } @RequestMapping("/get") public void testA(int id){ userService.get(id); } } @Service("userService") Class UserService{ public static Map<Integer,User> usersCache = new HashMap<String,User>(); public void add(User user){ usersCache.put(user.getId(),user); } public void get(int id){ usersCache.get(id); } }
此段代碼,usersCache對象就是線程不安全的。因為它是靜態的全局共享對象。如果有多個線程同時調用add方法,可能會發生用戶對象被覆蓋的情況,也就是id對應對象不一致,這是多線程編程中最常發生的事情。
所以,可以使用 Collections 工具同步Map。
static Map<Integer, Users> usersCache = Collections.synchronizedMap(new HashMap<Integer, Users>());
研究一下,Spring中的源碼,它對常用的開源框架做了大量封裝,如,Hibernate中的sessionFactory,就使用的是 org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean,而在 AnnotationSessionFactoryBean的父類LocalSessionFactoryBean中,定義了大量的ThreadLocal來保證多線程的安全性。
public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implements BeanClassLoaderAware { private static final ThreadLocal<DataSource> configTimeDataSourceHolder = new ThreadLocal<DataSource>(); private static final ThreadLocal<TransactionManager> configTimeTransactionManagerHolder = new ThreadLocal<TransactionManager>(); private static final ThreadLocal<Object> configTimeRegionFactoryHolder = new ThreadLocal<Object>(); private static final ThreadLocal<CacheProvider> configTimeCacheProviderHolder = new ThreadLocal<CacheProvider>(); private static final ThreadLocal<LobHandler> configTimeLobHandlerHolder = new ThreadLocal<LobHandler>();