Spring框架里的bean,或者說組件,獲取實例的時候都是默認的單例模式,這是在多線程開發的時候要尤其注意的地方。
每一個request過來,系統都會調用原有的instance去處理,這樣導致有倆個結果:
一是不用每次創建Controller
二是減少了對象創建和垃圾的收集的時間
當多用戶同時請求一個服務的時候,容器會給每一個請求分配一個線程,並發執行該業務邏輯,如果邏輯中有處理該單列的成員屬性時候,必須考慮線程同步。
考慮如下代碼:
@RequestMapping("/user")
@Controller
Class UserController
{
@Resource
UserService userService;
@RequestMapping("/add")
public void testA(User user){
userService.add(user);
}
@RequestMapping("/get")
public void testA(int id){
userService.get(id);
}
}
@Service("userService")
Class UserService{
public static Map<Integer,User> usersCache = new HashMap<String,User>();
public void add(User user){
usersCache.put(user.getId(),user);
}
public void get(int id){
usersCache.get(id);
}
}
此段代碼,usersCache對象就是線程不安全的。因為它是靜態的全局共享對象。如果有多個線程同時調用add方法,可能會發生用戶對象被覆蓋的情況,也就是id對應對象不一致,這是多線程編程中最常發生的事情。
Spring 框架維護 Controller Dao Service(注入) 的(方法)線程安全。
SpringMvc和Servlet都是方法的線程安全,在類方法聲明的公有和私有變量不是線程安全的。
如果用多個線程請求同一個Controller類中的同一個方法,線程不會阻塞。
Spring 框架使用了大量的ThreadLocal進制同步線數據 (空間換時間)
ThreadLocal 會為每一個線程提供一個獨立的變量副本:
class SimpleThreadLocal {
private MapvalueMap = Collections.synchronizedMap(new HashMap());
public voidset(Object newValue) {
valueMap.put(Thread.currentThread(), newValue);//①鍵為線程對象,值為本線程的變量副本
}
publicObject get() {
Thread currentThread = Thread.currentThread();
Object o = valueMap.get(currentThread);// ②返回本線程對應的變量
if (o == null &&!valueMap.containsKey(currentThread)) {// ③如果在Map中不存在,放到Map
// 中保存起來。
o = initialValue();
valueMap.put(currentThread, o);
}
return o;
}
public voidremove() {
valueMap.remove(Thread.currentThread());
}
publicObject initialValue() {
return null;
}
}
線程安全問題都是由全局變量及靜態變量引起的。