Spring Boot執行緒安全指南
答案是它取決於作用域: 決定元件執行緒安全性的主要因素是其作用域Scope。
哪個Spring作用域是執行緒安全的?
為了回答這個問題,首先需要了解Spring何時建立新執行緒。
在基於servlet的標準Spring Web應用程式中,每個新的HTTP請求都會生成一個新執行緒。如果容器為特定請求建立一個新的bean例項,我們可以說這個bean是執行緒安全的。
讓我們來看一下Spring中的作用域,並關注容器何時建立它們。
Spring單例執行緒安全嗎?
簡短的回答是:不
這是因為單例Bean的生命週期很長。這些bean可能會在來自不同使用者的許多HTTP請求中反覆使用。如果不使用@Lazy ,框架會在應用程式啟動時建立唯一的一個bean例項,並確保使用者會自動連線並重用相同的這個例項。只要容器存在,這個單例Bean例項一直會存在。
但框架並不控制單例的使用方式。如果兩個不同的執行緒同時執行單例的方法,則不能保證兩個呼叫都將同步並在能順序執行。(需要synchronize等鎖才能實現同步)
換句話說,您有責任確保您的程式碼在多執行緒環境中安全執行。Spring不會為你做這事。
請求級別作用域Request scope
如果你想確保你的bean是執行緒安全的,你應該使用@RequestScope,顧名思義,Spring將這種bean例項繫結到特定的Web請求。
這種bean例項不在多個執行緒之間共享,因此您不必關心併發。
但是等一下。
如果這種bean的併發很大,建立bean的新例項就比重用現有例項要慢。這時候,使用單例Bean,除非你有一個真正的用例場景可以使用RequestScope的bean。
會話級別作用域
Spring將會話bean與特定使用者關聯。當新使用者訪問您的應用程式時,將建立一個新的會話Bean例項,併為該使用者的所有請求重用該例項。
如您所知,某些使用者的請求可能是併發的。因此,會話bean不是執行緒安全的。它們的生命週期比請求作用域bean長。多個請求可以同時呼叫同一個會話bean。
prototype Bean
我把原型範圍作為最後討論的範圍,因為我們無法清楚地說它始終是執行緒安全的。Prototype的執行緒安全性取決於包含原型的bean的作用域。
只要使用者需要這個Bean的例項,Spring就會根據需要建立原型bean。(類似new object一樣呼叫一次建立一次);
想象一下,你的應用程式中有兩個bean。一個是單例Bean,第二個是請求作用域的bean。兩者都依賴於第三個原型的bean。
讓我們先考慮單例bean:因為單例不是執行緒安全的,所以對其原型方法的呼叫也可以同時執行。當多個執行緒共享單例時,Spring注入該單例的原型的單個例項也將被共享。
對於請求作用域的bean:Spring為每個Web請求建立此類元件的新例項。每個請求都繫結到一個單獨的執行緒。因此,請求bean的每個例項都獲得自己的原型bean例項。在這種情況下,您可以將原型視為執行緒安全的。
那麼Spring Web控制器是否是執行緒安全的?
這取決於這種控制器的作用域。
如果將控制器定義為預設的單例bean,則它不是執行緒安全的。將預設作用域更改為會話級別的,也不會使控制器安全。但是,請求作用域將使控制器bean安全地用於併發Web請求。
如果將控制器定義為原型bean,因為我們從不將控制器注入其他Bean,它們是我們應用程式的入口點。那麼當您將控制器定義為原型bean時,Spring的行為如何?
當您將控制器定義為原型時,Spring框架將為每個Web請求建立一個新例項。除非將它們注入不安全的作用域bean,否則可以將原型作用域的控制器視為執行緒安全的。
如何使任何Spring bean執行緒安全?
可以做的最好的辦法是解決訪問同步問題。
怎麼做?
使您的bean類變成無狀態。(banq注:又回到了EJB的無狀態bean和有態Bean,無狀態實際是不可變)
如果bean的方法執行不修改其例項的欄位屬性,則bean是無狀態的。
更改方法內的區域性變數是完全可以的,因為對方法的每次呼叫都會為這些變數分配記憶體。與在所有非靜態方法之間共享的例項欄位不同。
完美的無狀態bean沒有欄位,但你不會經常看到這樣的實用程式類。通常,您的bean有一些欄位。但是透過應用一些簡單的規則,您可以使任何bean無狀態且執行緒安全。
如何使Spring bean無狀態?
將所有bean欄位設定為final,以指示在bean欄位的生命週期中不應再次重新分配。
但是不要將欄位修改與重新分配混淆!使所有bean的欄位final不會使它成為無狀態。如果在執行時期間可以更改分配給bean的最終欄位的值,則此類bean仍然不是執行緒安全的。
比如使用final String, 無法更改String欄位的值,String類是不可變的,就像Integer,Boolean和其他原始包裝器一樣。在這種情況下,您還可以安全地使用基本型別。但是更復雜的物件如Collection,Map或自定義資料類呢?
對於像集合這樣的常見型別,您可以使用標準Java庫中可以找到的不可變實現。您可以使用Java 9中新增的工廠方法輕鬆建立不可變集合。如果您仍使用舊版本,請不要擔心。您還可以在Collections類中找到轉換方法,如unmodifiableList()。
如果涉及自定義資料型別,則必須確保它們是不可變的。在Java中建立不可變類超出了本文的範圍。(banq注:業務型別儘量使用值物件)
有狀態Spring bean中的執行緒安全變數
無狀態bean聽起來像銀彈。但是,如果您已經擁有有狀態bean並且必須在其中一個欄位上同步訪問許可權呢?
在這種情況下,您有一個經典的Java問題,即對類欄位的併發修改訪問。Spring框架不會為您解決它。您需要選擇一種可能的解決方案:
- synchronized 關鍵字和鎖定-此選項使您可以訪問同步的最大控制,但還需要更深入的瞭解在並行環境中使用的機制。
- 原子變數 - 您可以在Java標準庫中找到一小組執行緒安全型別。該包中的型別可以安全地用作共享有狀態bean中的欄位。
- 併發集合 - 除了原子變數之外,Java還為我們提供了一些有用的集合,我們可以使用它們而不必擔心併發訪問問題。
但請注意:無論您選擇哪種方法,訪問同步始終會對效能產生影響。如果您有其他選擇,請儘量避免使用它。
在Spring元件中實現執行緒安全的方法
正如我們已經討論過的,Spring本身並沒有解決併發訪問的問題。如果bean的範圍不是執行緒安全的,但其方法包含一些您總是希望安全執行的關鍵程式碼,請在該方法上使用synchronized關鍵字。
結論
我們需要知道Spring框架在多執行緒環境中的情況。必須自行提供執行緒安全性時的保障。
banq:其實可變資料或狀態都是儲存資料庫,如果將資料庫作為業務核心,就不必擔心多執行緒問題,但是六邊形和乾淨架構中,需要將資料庫作為技術放到業務核心之外,在這種架構下,就需要多注意多執行緒問題。在普通場景下多執行緒問題基本由資料庫技術解決了。本文問題只適合作為面試問答。
相關文章
- java安全編碼指南之:執行緒安全規則Java執行緒
- 執行緒3--執行緒安全執行緒
- 執行緒安全和執行緒不安全理解執行緒
- 執行緒安全執行緒
- Spring Boot使用執行緒池處理事務任務Spring Boot執行緒
- 多執行緒系列之 執行緒安全執行緒
- iOS 多執行緒之執行緒安全iOS執行緒
- iOS多執行緒之執行緒安全iOS執行緒
- Java執行緒(一):執行緒安全與不安全Java執行緒
- 【多執行緒總結(二)-執行緒安全與執行緒同步】執行緒
- 什麼是執行緒安全和執行緒不安全執行緒
- Python執行緒指南Python執行緒
- 【Java多執行緒】執行緒安全的集合Java執行緒
- 執行緒安全(二)執行緒
- Java執行緒安全Java執行緒
- Java - 執行緒安全Java執行緒
- Java 多執行緒基礎(四)執行緒安全Java執行緒
- iOS多執行緒安全-13種執行緒鎖?iOS執行緒
- Posix執行緒程式設計指南(3)-執行緒同步 (轉)執行緒程式設計
- 多執行緒,你覺得你安全了?(執行緒安全問題)執行緒
- PHP的執行緒安全與非執行緒安全版本的區別PHP執行緒
- 聊一聊Spring中的執行緒安全性Spring執行緒
- 執行緒安全性執行緒
- 多執行緒安全(一)執行緒
- 執行緒安全操作HashMap執行緒HashMap
- java執行緒安全LockJava執行緒
- EntityFrameworkDbContext執行緒安全FrameworkContext執行緒
- strerror執行緒安全分析Error執行緒
- 容器不是執行緒安全執行緒
- 併發程式設計之多執行緒執行緒安全程式設計執行緒
- 小度分享-【多執行緒工作及執行緒安全】執行緒
- 多執行緒與高併發(二)執行緒安全執行緒
- iOS開發基礎——執行緒安全(執行緒鎖)iOS執行緒
- 併發與多執行緒之執行緒安全篇執行緒
- 如何執行Spring Boot專案Spring Boot
- spring boot 執行sql檔案Spring BootSQL
- Posix執行緒程式設計指南(4)-執行緒終止 (轉)執行緒程式設計
- Spring Boot 參考指南(Spring Boot文件)Spring Boot