Spring 中的bean 是線程安全的嗎?

2020-12-24 計算機java編程

結論:不是線程安全的

Spring容器中的Bean是否線程安全,容器本身並沒有提供Bean的線程安全策略,因此可以說Spring容器中的Bean本身不具備線程安全的特性,但是具體還是要結合具體scope的Bean去研究。

Spring 的 bean 作用域(scope)類型

singleton:單例,默認作用域。prototype:原型,每次創建一個新對象。request:請求,每次Http請求創建一個新對象,適用於WebApplicationContext環境下。session:會話,同一個會話共享一個實例,不同會話使用不用的實例。global-session:全局會話,所有會話共享一個實例。線程安全這個問題,要從單例與原型Bean分別進行說明。

原型Bean

對於原型Bean,每次創建一個新對象,也就是線程之間並不存在Bean共享,自然是不會有線程安全的問題。

單例Bean

對於單例Bean,所有線程都共享一個單例實例Bean,因此是存在資源的競爭。

如果單例Bean,是一個無狀態Bean,也就是線程中的操作不會對Bean的成員執行查詢以外的操作,那麼這個單例Bean是線程安全的。比如Spring mvc 的 Controller、Service、Dao等,這些Bean大多是無狀態的,只關注於方法本身。

spring單例,為什麼controller、service和dao確能保證線程安全?

Spring中的Bean默認是單例模式的,框架並沒有對bean進行多線程的封裝處理。

實際上大部分時間Bean是無狀態的(比如Dao) 所以說在某種程度上來說Bean其實是安全的。

但是如果Bean是有狀態的 那就需要開發人員自己來進行線程安全的保證,最簡單的辦法就是改變bean的作用域 把 "singleton"改為』『protopyte』 這樣每次請求Bean就相當於是 new Bean() 這樣就可以保證線程的安全了。

有狀態就是有數據存儲功能無狀態就是不會保存數據 controller、service和dao層本身並不是線程安全的,只是如果只是調用裡面的方法,而且多線程調用一個實例的方法,會在內存中複製變量,這是自己的線程的工作內存,是安全的。

Java虛擬機棧是線程私有的,它的生命周期與線程相同。虛擬機棧描述的是Java方法執行的內存模型:每個方法在執行的同時都會創建一個棧幀用於存儲局部變量表、操作數棧、動態連結、方法出口等信息。

局部變量的固有屬性之一就是封閉在執行線程中。它們位於執行線程的棧中,其他線程無法訪問這個棧。

所以其實任何無狀態單例都是線程安全的。

Spring的根本就是通過大量這種單例構建起系統,以事務腳本的方式提供服務。

首先問@Controller @Service是不是線程安全的?

答:默認配置下不是的。為啥呢?因為默認情況下@Controller沒有加上@Scope,沒有加@Scope就是默認值singleton,單例的。意思就是系統只會初始化一次Controller容器,所以每次請求的都是同一個Controller容器,當然是非線程安全的。舉個慄子:

在postman裡面發三次請求,結果如下:

說明他不是線程安全的。怎麼辦呢?可以給他加上上面說的@Scope註解,如下:

這樣一來,每個請求都單獨創建一個Controller容器,所以各個請求之間是線程安全的,三次請求結果:

加了@Scope註解多的實例prototype是不是一定就是線程安全的呢?

看三次請求結果:

雖然每次都是單獨創建一個Controller但是扛不住他變量本身是static的呀,所以說呢,即便是加上@Scope註解也不一定能保證Controller 100%的線程安全。所以是否線程安全在於怎樣去定義變量以及Controller的配置。

所以來個全乎一點的實驗,代碼如下:

補充Controller以外的代碼:

config裡面自己定義的Bean:User

我暫時能想到的定義變量的方法就這麼多了,三次http請求結果如下:

可以看到,在單例模式下Controller中只有用ThreadLocal封裝的變量是線程安全的。為什麼這樣說呢?我們可以看到3次請求結果裡面只有ThreadLocal變量值每次都是從0+1=1的,其他的幾個都是累加的,而user對象呢,默認值是0,第二交取值的時候就已經是1了,關鍵他的hashCode是一樣的,說明每次請求調用的都是同一個user對象。

下面將TestController 上的@Scope註解的屬性改一下改成多實例的:@Scope(value = "prototype"),其他都不變,再次請求,結果如下:

分析這個結果發現,多實例模式下普通變量,取配置的變量還有ThreadLocal變量都是線程安全的,而靜態變量和user(看他的hashCode都是一樣的)對象中的變量都是非線程安全的。

也就是說儘管TestController 是每次請求的時候都初始化了一個對象,但是靜態變量始終是只有一份的,而且這個注入的user對象也是只有一份的。靜態變量只有一份這是當然的咯,那麼有沒有辦法讓user對象可以每次都new一個新的呢?當然可以:

在config裡面給這個注入的Bean加上一個相同的註解@Scope(value = "prototype")就可以了,再來請求一下看看:

可以看到每次請求的user對象的hashCode都不是一樣的,每次賦值前取user中的變量值也都是默認值0。

下面總結一下:

1、在@Controller/@Service等容器中,默認情況下,scope值是單例-singleton的,也是線程不安全的。

2、儘量不要在@Controller/@Service等容器中定義靜態變量,不論是單例(singleton)還是多實例(prototype)他都是線程不安全的。

3、默認注入的Bean對象,在不設置scope的時候他也是線程不安全的。

4、一定要定義變量的話,用ThreadLocal來封裝,這個是線程安全的

相關焦點

  • Spring中的線程安全問題
    一:Spring與線程安全Spring作為一個IOC/DI容器,幫助我們管理了許許多多的「bean」。但其實,Spring並沒有保證這些對象的線程安全,需要由開發者自己編寫解決線程安全問題的代碼。Spring對每個bean提供了一個scope屬性來表示該bean的作用域。
  • spring中都有什麼作用域的bean
    如果問spring中都有什麼作用域的bean、相信很多人都會回答「單例」,對的,如果這樣你只回答了一種。spring中默認所有的bean都是作為單例(singleton)的形式創建的。不管bean被注入到其他的bean多少次,每次注入的都是同一個實例。
  • spring面試題目!
    Spring 框架中都用到了哪些設計模式?詳細講解一下核心容器(spring context應用上下文) 模塊Spring框架中有哪些不同類型的事件Spring 應用程式有哪些不同組件?使用 Spring 有哪些方式?
  • 面試刷題:Spring Bean的生命周期?
    spring是Java軟體開發的事實標準。今天的問題是:springBean的生命周期是怎樣的?bean的生命周期完全由spring容器管理,從屬性設置到各種依賴關係的注入,簡化了開發人員對bean的生命周期認知;Spring的容器中Bean生命周期如下對象創建1、從xml配置的Bean
  • 再談Spring中Bean的生命周期
    一、Bean 的完整生命周期在傳統的Java應用中,bean的生命周期很簡單,使用Java關鍵字 new 進行Bean 的實例化,然後該Bean 就能夠使用了。一旦bean不再被使用,則由Java自動進行垃圾回收。
  • 再見面試官:你能說說 Spring 框架中 Bean 的生命周期嗎?
    ><beans xmlns=」http://www.springframework.org/schema/beans」xmlns:xsi=」http://www.w3.org/2001/XMLSchema-instance」xsi:schemaLocation=」http://www.springframework.org/schema/beans http
  • Spring必須掌握的Bean增強擴展點與加載流程
    用來將註解@Configuration中的相關生成bean的方法所對應的BeanDefinition進行註冊。org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator,基於beanName創建代理,就是應用了這個接口,在生成bean前生成代理bean,從而替代默認的實例化。
  • 面試刷題30:SpringBean的生命周期?
    spring是Java軟體開發的事實標準
  • Spring中這些能升華代碼的技巧,可能會讓你愛不釋手
    有些讀者私信我說希望後面多分享spring方面的文章,這樣能夠在實際工作中派上用場。正好我對spring源碼有過一定的研究,並結合我這幾年實際的工作經驗,把spring中我認為不錯的知識點總結一下,希望對您有所幫助。
  • 面試官:你了解spring嗎?spring的兩大核心是什麼?
    創建bean類,並在spring中進行配置交由spring來管理1 <?xml version="1.0" encoding="UTF-8"?bean在單例模式下的生命周期:bean在單例模式下,spring容器啟動時解析xml發現該bean標籤後,直接創建該bean的對象存入內部map中保存,此後無論調用多少次getBean()獲取該bean都是從map中獲取該對象返回,一直是一個對象。
  • java spring boot 面試題 高級 bean 生命周期
    java spring boot 面試題 高級 bean 生命周期過程如下, 核心步驟為如下高亮四步.調用BeanFactoryAware的setBeanFactory方法執行BeanPostProcessor的postProcessBeforeInitialization方法調用Initializ ingBean的afterPropertiesSet方法調用<bean
  • 探究Spring 的定時任務配置
    MethodInvokingJobDetailFactoryBean配置需要定時執行的任務類和方法,targetObject為執行定時任務的bean,targetMethod為執行定時任務的bean中的方法。CronTriggerFactoryBean為定時任務的觸發器,用來配置定時任務執行的時間表達式。
  • 安全的Spring Cloud配置
    我的GitHub存儲庫sample-spring-cloud-security在分支secure_config中提供了示例應用程式原始碼:https : //github.com/piomin/sample-spring-cloud-security/tree/secure_config。
  • Java經典面試題Spring是什麼 Spring框架入門詳解
    創建下面一個pojo看看spring都有哪些功能Sping有一個功能就是幫我們創建pojo對象實例,下面我麼需要在applicationContext.xml中將pojo配置為bean,註冊到spring
  • 阿里面試總結:69道必問的spring面試題(附加答案)
    什麼是spring?2. 使用Spring框架的好處是什麼?3. Spring由哪些模塊組成?4. 核心容器(應用上下文) 模塊。5. BeanFactory – BeanFactory 實現舉例。解釋Spring支持的幾種bean的作用域。26. Spring框架中的單例bean是線程安全的嗎?27. 解釋Spring框架中bean的生命周期。28. 哪些是重要的bean生命周期方法? 你能重載它們嗎?29.
  • OSGi與Spring DM:使用Spring DataSource
    在***這個小節中,我們將學習Spring DM如何允許用戶訪問基於OSGi應用程式中的這些預製bean。在這個練習中,我們將修改 ContactDAO套件,使它連接到一個RDBMS,來與聯絡表互動。在這個練習中,我使用 Apache Derby 來測試應用程式的代碼;你也可以使用你喜歡的資料庫。該練習的***步是在你的資料庫中創建聯絡表。可以使用代碼清單 11 中的 DDL 腳本創建這個 CONTACT 表。代碼清單 11.
  • Java編程中的Spring多例
    在Java開發中,Spring是用得最多的一個框架了。spring的bean默認是單例,這個用spring的人基本都知道。如果需要多個實例,又要使用ioc怎麼辦呢?>當然是使用@Scope註解,指明ConfigurableBeanFactory.SCOPE_PROTOTYPE就可以了給一個組件加上@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)在使用過程中發現依然是單例
  • 細說框架每日一解Spring @Bean Annotaion
    如果我們想要 Spring 在啟動的時候延遲加載 bean,即在調用某個 bean 的時候再去初始化,那麼就可以使用 @Lazy 註解。依賴於另外一個bean。所依賴的bean會被容器確保在當前bean實例化之前被實例化。
  • Bean複製的幾種框架性能比較(BeanUtils、PropertyUtils、BeanCopier)
    (m.getMethodName() + "耗時" + (end - begin));            System.out.println(tobean.getAddress());            System.out.println(tobean.getAge());            System.out.println(tobean.getIdno(
  • Spring學習筆記,菜鳥程序猿必看,一篇文章讓你摸透Spring
    www.springframework.org/schema/beans/spring-beans.xsd"> //在此配置bean <bean id="userService"> </bean></beans>調用類:ApplicationContext applicationContext