spring管理bean时默认的单例是线程安全的吗?

话说: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的生命周期范围内。

温馨提示:答案为网友推荐,仅供参考
第1个回答  2014-01-10
单例的线程肯定不安全,所以action要配制成原型模式

相关了解……

你可能感兴趣的内容

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 非常风气网