1. 程式人生 > 其它 >spring bean解決單例是併發不安全的問題

spring bean解決單例是併發不安全的問題

解決方案

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。