SessionFactory負責創建session,SessionFactory是線程安全的,多個並發線程可以同時訪問一個
SessionFactory 並從中獲取Session實例。 (SessionFactory源碼大部分是final修飾的)

而Session並非線程安全,也就是說,如果多個線程同時使用一個Session實例進行數據存取,則將會導致 Session 數據存取邏輯混亂.因此創建的Session實例必須在本地存取空上運行,使之總與當前的線程相關。這里就需要用到ThreadLocal,在很多種Session 管理方案中都用到了它.ThreadLocal 是Java中一種較為特殊的線程綁定機制,通過ThreadLocal存取的數據, 總是與當前線程相關, 也就是說,JVM 為每個運行的線程,綁定了私有的本地實例存取空間,從而為多線程環境常出現的並發訪問問題提供了一種隔離機制,ThreadLocal並不是線程本地化的實現,而是線程局部變量。也就是說每個使用該變量的線程都必須為該變量提供一個副本,每個線程改變該變量的值僅僅是改變該副本的值,而不會影響其他線程的該變量的值,ThreadLocal是隔離多個線程的數據共享,不存在多個線程之間共享資源,因此不再需要對線程同步。(session實現類)

千萬不要把session,或者 session里面的對象直接傳遞給業務層,因為你的業務處理一半時,同樣可能出現session對象被改變的情況。有可能造成重要數據出現偏差。
舉例:
session 對應三個產品,
事務里面循環了產品,並計算了總價格,
計算完畢,准備保存時,session變了,產品變成了4個。
此時開始保存。產品保存了4個,可總價格卻還是3個的。
出現了數據不一致。
修改后的例子
session 對應三個產品
重新生成一個產品對象數組,把session數據復制過來,然后傳遞給業務層
事務里面循環計算總價格
計算完畢,此時session變了,但並不影響我們這個新的產品數組對象
保存,三個產品,價格也正確。
- HttpSession session = request.getSession();
- List<Product> list = session.getAttribute("productCart");
- List<Product> listNew = new ArrayList<Product>();
- Product pNew;
- for(Product p : list){
- pNew = new Product();
- pNew.setProductId(p.getProductId());
- // 其它的復制參數的語句
- listNew.add(pNew); // 保存到新的列表里面
- }
- myService.save(listNew); // 保存購物車數據到數據庫,這個是安全的
總結:
有些線程安全問題是很隱蔽的,等你出了問題,很可能根本不認為會是那里出的問題。記住一點,Java里面的對象傳遞的是對象的引用,只要2個地方用了相同的引用,則其它地方的變動,這一面也會變動。

