解決方案
1.單例變原型
對web項目,可以Controller類上加注解@Scope("prototype")或@Scope("request")
2.線程隔離類ThreadLocal
有人想到了線程隔離類ThreadLocal,我們嘗試將成員變量包裝為ThreadLocal,以試圖達到並發安全,代碼如下:
@Controller
public class HomeController {
private ThreadLocal<Integer> i = new ThreadLocal<>();
@GetMapping("testsingleton1")
@ResponseBody
public int test1() {
if (i.get() == null) {
i.set(0);
}
i.set(i.get().intValue() + 1);
log.info("{} -> {}", Thread.currentThread().getName(), i.get());
return i.get().intValue();
}
}
總結:ThreadLocal的方式可以達到線程隔離,但還是無法達到並發安全。
3.盡量避免使用成員變量
有人說,單例bean的成員變量這么麻煩,能不用成員變量就盡量避免這么用,在業務允許的條件下,將成員變量替換為RequestMapping方法中的局部變量
4. 使用並發安全的類
Java作為功能性超強的編程語言,API豐富,如果非要在單例bean中使用成員變量,可以考慮使用並發安全的容器,如ConcurrentHashMap、ConcurrentHashSet等等等等,將我們的成員變量(一般可以是當前運行中的任務列表等這類變量)包裝到這些並發安全的容器中進行管理即可。
spring bean作用域有以下5個:
- singleton:單例模式,當spring創建applicationContext容器的時候,spring會欲初始化所有的該作用域實例,加上lazy-init就可以避免預處理;
- prototype:原型模式,每次通過getBean獲取該bean就會新產生一個實例,創建后spring將不再對其管理;
- request:搞web的大家都應該明白request的域了吧,就是每次請求都新產生一個實例,和prototype不同就是創建后,接下來的管理,spring依然在監聽;
- session:每次會話,同上;
- global session:全局的web域,類似於servlet中的application。
