在各種session 治理方案中, ThreadLocal 模式得到了大量使用。ThreadLocal 是java中一種較為非凡的線程綁定機制。通過ThreadLocal存取的數據,總是與當前線程相關,也就是說,JVM 為每個運行的線程,綁定了私有的本地實例存取空間,從而為多線程環境常出現的并發訪問問題提供了一種隔離機制。首先,我們需要知道,SessionFactory負責創建Session,SessionFactory是線程
安全的,多個并發線程可以同時訪問一個SessionFactory 并從中獲取Session 實例。而
Session并非線程安全,也就是說,假如多個線程同時使用一個Session實例進行數據存取,
則將會導致Session 數據存取邏輯混亂。下面是一個典型的Servlet,我們試圖通過一個類
變量session實現Session的重用,以避免每次操作都要重新創建:
public class TestServlet extends HttpServlet {
PRivate Session session;
public void doGet( HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
session = getSession();
doSomething();
session.flush();
}
public void doSomething(){
......//基于session的存取操作
}
}
代碼看上去正確無誤,甚至在我們單機測試的時候可能也不會發生什么問題,但這樣的代
Hibernate Developer's Guide Version 1.0
September 2, 2004 So many open source projects. Why not Open your Documents?
碼一旦編譯部署到實際運行環境中,接踵而來的莫名其妙的錯誤很可能會使得我們摸不找頭腦。
問題出在哪里?
首先,Servlet 運行是多線程的,而應用服務器并不會為每個線程都創建一個Servlet
實例,也就是說,TestServlet在應用服務器中只有一個實例(在Tomcat中是這樣,其他的
應用服務器可能有不同的實現),而這個實例會被許多個線程并發調用,doGet 方法也將被不
同的線程反復調用,可想而知,每次調用doGet 方法,這個唯一的TestServlet 實例的
session 變量都會被重置,線程A 的運行過程中,其他的線程假如也被執行,那么session
的引用將發生改變,之后線程A 再調用session,可能此時的session 與其之前所用的
session就不再一致,顯然,錯誤也就不期而至。
ThreadLocal的出現,使得這個問題迎刃而解。
我們對上面的例子進行一些小小的修改:
public class TestServlet extends HttpServlet {
private ThreadLocal localSession = new ThreadLocal();
public void doGet( HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
localSession.set(getSession());
doSomething();
session.flush();
}
public void doSomething(){
Session session = (Session)localSession.get();
......//基于session的存取操作
}
}
可以看到,localSession 是一個ThreadLocal 類型的對象,在doGet 方法中,我們
通過其set 方法將獲取的session 實例保存,而在doSomething 方法中,通過get 方法取
出session實例。
這也就是ThreadLocal的獨特之處,它會為每個線程維護一個私有的變量空間。實際上,
其實現原理是在JVM 中維護一個Map,這個Map的key 就是當前的線程對象,而value則是
線程通過ThreadLocal.set方法保存的對象實例。當線程調用ThreadLocal.get方法時,
ThreadLocal會根據當前線程對象的引用,取出Map中對應的對象返回。
這樣,ThreadLocal通過以各個線程對象的引用作為區分,從而將不同線程的變量隔離開
來。
Hibernate官方開發手冊的示例中,提供了一個通過ThreadLocal維護Session的好
榜樣:
public class HibernateUtil {
private static SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory
sessionFactory = new
Configuration().configure().buildSessionFactory();
} catch (HibernateException ex) {
throw new RuntimeException(
"Configuration problem: " + ex.getMessage(),
ex
);
}
}
public static final ThreadLocal session = new ThreadLocal();
public static Session currentSession() throws HibernateException
新聞熱點
疑難解答