1.Spring單例模式與線程安全
Spring框架里的bean或者component,在獲取實例時都是默認的單例模式。單例模式確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例。
當多用戶同時請求一個服務時,容器會給每一個請求分配一個線程,並使用ThreadLocal,從而保證系統的性能。
ThreadLocal和線程同步機制相比有什么優勢呢?
在同步機制中,通過對象的鎖機制保證同一時間只有一個線程訪問變量。這時該變量是多個線程共享的,使用同步機制要求程序慎密地分析什么時候對變量進行讀寫,什么時候需要鎖定某個對象,什么時候釋放對象鎖等繁雜的問題,程序設計和編寫難度相對較大。
而ThreadLocal則從另一個角度來解決多線程的並發訪問。ThreadLocal會為每一個線程提供一個獨立的變量副本,從而隔離了多個線程對數據的訪問沖突。因為每一個線程都擁有自己的變量副本,從而也就沒有必要對該變量進行同步了。ThreadLocal提供了線程安全的共享對象,在編寫多線程代碼時,可以把不安全的變量封裝進ThreadLocal。
概括起來說,對於多線程資源共享的問題,同步機制采用了“以時間換空間”的方式,而ThreadLocal采用了“以空間換時間”的方式。前者僅提供一份變量,讓不同的線程排隊訪問,而后者為每一個線程都提供了一份變量,因此可以同時訪問而互不影響。
2.Spring MVC Controller單例陷阱
單例模式下:
@RestController @RequestMapping("controller") public class TestController { private static int index_s = 0;//靜態的 private int index = 0;//非靜態 @GetMapping("/test") public String test() { return index_s++ + " | " + index++; } }
結果:
0 | 0 1 | 1 2 | 2 3 | 3 4 | 4
多例模式下:
@RestController @RequestMapping("controller") @Scope("prototype") public class TestController { private static int index_s = 0;//靜態的 private int index = 0;//非靜態 @GetMapping("/test") public String test() { return index_s++ + " | " + index++; } }
結果:
0 | 0 1 | 0 2 | 0 3 | 0 4 | 0
由此可見:
單例是線程不安全的,會導致屬性的重復性利用。
1、不要在controller中定義成員變量。
2、萬一必須要定義一個非靜態成員變量時候,則通過注解@Scope("prototype"),將其設置為多例模式
參考資料:
https://blog.51cto.com/lavasoft/1394669
https://blog.csdn.net/paladinzh/article/details/88051356