话说:Spring 通过 ThreadLocal 将有状态的变量本地线程化,达到另一个层面上的“线程无关”,从而实现线程安全。
问题:那么如果某个bean拥有一个成员变量(非静态成员变量),在单例多线程的情况下就可能会出现多个线程共享这个成员变量的情况,是不是就会出现线程安全问题?但是想想如果spring都能做到将有状态的变量本地线程化,那么肯定也就做到了把bean中的其他成员变量也本地线程化,从而实现线程安全吧。
但是又有话说:
ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本。通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为map的key来使用的。 如果ThreadLocal.set()进去的东西本来就是多个线程共享的同一个对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题。
如果这种说法是正确的,那么spring是不是不能通过ThreadLocal将单例多线程情况下共享的成员变量本地线程化,从而实现线程安全,如果是那spring又是用何种魔力让有状态的成员变量本地线程化实现线程安全呢 ?
或者说是第二个说法是正确的而且spring单例也绝对是线程安全地,因为spring是让ThreadLocal.set()进去的东西全都是新创建的对象,对于成员变量也都是从新new的,那这样以来,创建的成员变量岂不是没有任何意义的(除不可改变的类成员变量)?
又但是如果上面的或者说是正确的,那么在使用spring时,通常action中会调用service,调用的service写在方法外,我认为它也是一个成员变量,而我们的service通常也是交给spring去管理的,一般也就会是默认的单例的,既然spring将其管理为单例的,那么它不至于在多线程情况下又从新把调用的service从新new一下吧(这个绝对不可能),既然如此,那么spring到底如何让有状态的成员变量本地线程化实现线程安全,如何让多线程共享成员变量时自动线程安全?
1ãBeançç®ä»
å¨Springä¸ï¼é£äºç»æåºç¨ç¨åºç主ä½(backbone)åç±Spring IoC容å¨æ管çç对象ï¼è¢«ç§°ä¹ä¸ºbeanã ç®åå°è®²ï¼beanå°±æ¯ç±Spring容å¨åå§åãè£ é å管çç对象ï¼é¤æ¤ä¹å¤ï¼beanå°±ä¸åºç¨ç¨åºä¸çå ¶ä»å¯¹è±¡æ²¡æä»ä¹åºå«äºã èbeanå®ä¹ä»¥åbeanç¸äºé´çä¾èµå ³ç³»å°éè¿é ç½®å æ°æ®æ¥æè¿°ã
2ãBeançä½ç¨å
å建ä¸ä¸ªbeanå®ä¹ï¼å ¶å®è´¨æ¯ç¨è¯¥beanå®ä¹å¯¹åºçç±»æ¥å建çæ£å®ä¾çâé æ¹(recipe)âãæbeanå®ä¹çæä¸ä¸ªé æ¹å¾ææä¹ï¼å®ä¸classå¾ç±»ä¼¼ï¼åªæ ¹æ®ä¸å¼ âå¤æ¹âå°±å¯ä»¥å建å¤ä¸ªå®ä¾ã
ä¸ä» å¯ä»¥æ§å¶æ³¨å ¥å°å¯¹è±¡ä¸çåç§ä¾èµåé ç½®å¼ï¼è¿å¯ä»¥æ§å¶è¯¥å¯¹è±¡çä½ç¨åãè¿æ ·å¯ä»¥çµæ´»éæ©æ建对象çä½ç¨åï¼èä¸å¿ å¨Java Class级å®ä¹ä½ç¨åãSpring Frameworkæ¯æäºç§ä½ç¨åï¼å ¶ä¸æä¸ç§åªè½ç¨å¨åºäºwebçSpring ApplicationContextï¼ã
1ï¼singleton
å½ä¸ä¸ªbeançä½ç¨å为singleton, é£ä¹Spring IoC容å¨ä¸åªä¼åå¨ä¸ä¸ªå ±äº«çbeanå®ä¾ï¼å¹¶ä¸ææ对beanç请æ±ï¼åªè¦idä¸è¯¥beanå®ä¹ç¸å¹é ï¼ååªä¼è¿åbeançåä¸å®ä¾ã
注æï¼Singletonä½ç¨åæ¯Springä¸ç缺çä½ç¨åãè¦å¨XMLä¸å°beanå®ä¹æsingletonï¼å¯ä»¥è¿æ ·é ç½®ï¼
<bean id="empServiceImpl" class="cn.csdn.service.EmpServiceImpl" scope="singleton">2ï¼prototype
ä¸ä¸ªbeanå®ä¹å¯¹åºå¤ä¸ªå¯¹è±¡å®ä¾ãPrototypeä½ç¨åçbeanä¼å¯¼è´å¨æ¯æ¬¡å¯¹è¯¥bean请æ±ï¼å°å ¶æ³¨å ¥å°å¦ä¸ä¸ªbeanä¸ï¼æè 以ç¨åºçæ¹å¼è°ç¨å®¹å¨çgetBean()æ¹æ³ï¼æ¶é½ä¼å建ä¸ä¸ªæ°çbeanå®ä¾ãæ ¹æ®ç»éªï¼å¯¹æç¶æçbeanåºè¯¥ä½¿ç¨prototypeä½ç¨åï¼è对æ ç¶æçbeanååºè¯¥ä½¿ç¨singletonä½ç¨åã
3ï¼request
å¨ä¸æ¬¡HTTP请æ±ä¸ï¼ä¸ä¸ªbeanå®ä¹å¯¹åºä¸ä¸ªå®ä¾ï¼å³æ¯æ¬¡HTTP请æ±å°ä¼æåèªçbeanå®ä¾ï¼ å®ä»¬ä¾æ®æ个beanå®ä¹å建èæã该ä½ç¨åä» å¨åºäºwebçSpring ApplicationContextæ å½¢ä¸ææã
èèä¸é¢beanå®ä¹ï¼
<bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>é对æ¯æ¬¡HTTP请æ±ï¼Spring容å¨ä¼æ ¹æ®loginAction beanå®ä¹å建ä¸ä¸ªå ¨æ°çLoginAction beanå®ä¾ï¼ ä¸è¯¥loginAction beanå®ä¾ä» å¨å½åHTTP requestå ææï¼å æ¤å¯ä»¥æ ¹æ®éè¦æ¾å¿çæ´æ¹æ建å®ä¾çå é¨ç¶æï¼ èå ¶ä»è¯·æ±ä¸æ ¹æ®loginAction beanå®ä¹å建çå®ä¾ï¼å°ä¸ä¼çå°è¿äºç¹å®äºæ个请æ±çç¶æååã å½å¤ç请æ±ç»æï¼requestä½ç¨åçbeanå®ä¾å°è¢«éæ¯ã
4ï¼session
å¨ä¸ä¸ªHTTP Sessionä¸ï¼ä¸ä¸ªbeanå®ä¹å¯¹åºä¸ä¸ªå®ä¾ã该ä½ç¨åä» å¨åºäºwebçSpring ApplicationContextæ å½¢ä¸ææã
èèä¸é¢beanå®ä¹ï¼
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>é对æ个HTTP Sessionï¼Spring容å¨ä¼æ ¹æ®userPreferences beanå®ä¹å建ä¸ä¸ªå ¨æ°çuserPreferences beanå®ä¾ï¼ ä¸è¯¥userPreferences beanä» å¨å½åHTTP Sessionå ææã ä¸requestä½ç¨åä¸æ ·ï¼å¯ä»¥æ ¹æ®éè¦æ¾å¿çæ´æ¹æå建å®ä¾çå é¨ç¶æï¼èå«çHTTP Sessionä¸æ ¹æ®userPreferenceså建çå®ä¾ï¼ å°ä¸ä¼çå°è¿äºç¹å®äºæ个HTTP Sessionçç¶æååã å½HTTP Sessionæç»è¢«åºå¼çæ¶åï¼å¨è¯¥HTTP Sessionä½ç¨åå çbeanä¹ä¼è¢«åºå¼æã
5ï¼global session
å¨ä¸ä¸ªå ¨å±çHTTP Sessionä¸ï¼ä¸ä¸ªbeanå®ä¹å¯¹åºä¸ä¸ªå®ä¾ãå ¸åæ åµä¸ï¼ä» å¨ä½¿ç¨portlet contextçæ¶åææã该ä½ç¨åä» å¨åºäºwebçSpring ApplicationContextæ å½¢ä¸ææã
èèä¸é¢beanå®ä¹ï¼
<bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/>global sessionä½ç¨å类似äºæ åçHTTP Sessionä½ç¨åï¼ä¸è¿ä» ä» å¨åºäºportletçwebåºç¨ä¸æææä¹ãPortletè§èå®ä¹äºå ¨å±Sessionçæ¦å¿µï¼å®è¢«æææææ个portlet webåºç¨çåç§ä¸åçportletæå ±äº«ãå¨global sessionä½ç¨åä¸å®ä¹çbean被éå®äºå ¨å±portlet Sessionççå½å¨æèå´å ã