Spring與線程安全

Spring做爲一個IOC/DI容器,幫助咱們管理了許許多多的「bean」。但其實,Spring並無保證這些對象的線程安全,須要由開發者本身編寫解決線程安全問題的代碼。web

Spring對每一個bean提供了一個scope屬性來表示該bean的做用域。它是bean的生命週期。例如,一個scope爲singleton的bean,在第一次被注入時,會建立爲一個單例對象,該對象會一直被複用到應用結束。數據庫

  • singleton:默認的scope,每一個scope爲singleton的bean都會被定義爲一個單例對象,該對象的生命週期是與Spring IOC容器一致的(但在第一次被注入時纔會建立)。
  • prototype:bean被定義爲在每次注入時都會建立一個新的對象。
  • request:bean被定義爲在每一個HTTP請求中建立一個單例對象,也就是說在單個請求中都會複用這一個單例對象。
  • session:bean被定義爲在一個session的生命週期內建立一個單例對象。
  • application:bean被定義爲在ServletContext的生命週期中複用一個單例對象。
  • websocket:bean被定義爲在websocket的生命週期中複用一個單例對象。

咱們交由Spring管理的大多數對象其實都是一些無狀態的對象,這種不會由於多線程而致使狀態被破壞的對象很適合Spring的默認scope,每一個單例的無狀態對象都是線程安全的(也能夠說只要是無狀態的對象,無論單例多例都是線程安全的,不過單例畢竟節省了不斷建立對象與GC的開銷)。安全

無狀態的對象便是自身沒有狀態的對象,天然也就不會由於多個線程的交替調度而破壞自身狀態致使線程安全問題。無狀態對象包括咱們常常使用的DO、DTO、VO這些只做爲數據的實體模型的貧血對象,還有Service、DAO和Controller,這些對象並無本身的狀態,它們只是用來執行某些操做的。例如,每一個DAO提供的函數都只是對數據庫的CRUD,並且每一個數據庫Connection都做爲函數的局部變量(局部變量是在用戶棧中的,並且用戶棧自己就是線程私有的內存區域,因此不存在線程安全問題),用完即關(或交還給鏈接池)。websocket

有人可能會認爲,我使用request做用域不就能夠避免每一個請求之間的安全問題了嗎?這是徹底錯誤的,由於Controller默認是單例的,一個HTTP請求是會被多個線程執行的,這就又回到了線程的安全問題。固然,你也能夠把Controller的scope改爲prototype,實際上Struts2就是這麼作的,但有一點要注意,Spring MVC對請求的攔截粒度是基於每一個方法的,而Struts2是基於每一個類的,因此把Controller設爲多例將會頻繁的建立與回收對象,嚴重影響到了性能。session

經過閱讀上文其實已經說的很清楚了,Spring根本就沒有對bean的多線程安全問題作出任何保證與措施。對於每一個bean的線程安全問題,根本緣由是每一個bean自身的設計。不要在bean中聲明任何有狀態的實例變量或類變量,若是必須如此,那麼就使用ThreadLocal把變量變爲線程私有的,若是bean的實例變量或類變量須要在多個線程之間共享,那麼就只能使用synchronized、lock、CAS等這些實現線程同步的方法了。

多線程

相關文章
相關標籤/搜索