源地址;http://sishuok.com/forum/blogPost/list/340.html
首先,ThreadLocal 不是用來解決共用對象的多線程訪問問題的,一般情況下,通過ThreadLocal.set() 到線程中的對象是該線程自己使用的對象,其他線程是不需要訪問的,也訪問不到的。各個線程中訪問的是不同的對象。
另外,說ThreadLocal使得各線程能夠保持各自獨立的一個對象,並不是通過ThreadLocal.set()來實現的,而是通過每個線程中的new 對象 的操作來建立的對象,每個線程建立一個,不是什麼對象的拷貝或副本。通過ThreadLocal.set()將這個新建立的對象的引用儲存到各線程的自己的一個map中,每個線程都有這樣一個map,執行ThreadLocal.get()時,各線程從自己的map中取出放進去的對象,因此取出來的是各自自己線程中的對象,ThreadLocal執行個體是作為map的key來使用的。
如果ThreadLocal.set()進去的東西本來就是多個線程共用的同一個對象,那麼多個線程的ThreadLocal.get()取得的還是這個共用對象本身,還是有並發訪問問題。
下面來看一個hibernate中典型的ThreadLocal的應用:
java代碼:
查看複製到剪貼簿列印
- private static final ThreadLocal threadSession = new ThreadLocal();
-
- public static Session getSession() throws InfrastructureException {
- Session s = (Session) threadSession.get();
- try {
- if (s == null) {
- s = getSessionFactory().openSession();
- threadSession.set(s);
- }
- } catch (HibernateException ex) {
- throw new InfrastructureException(ex);
- }
- return s;
- }
可以看到在getSession()方法中,首先判斷當前線程中有沒有放進去session,如果還沒有,那麼通過sessionFactory().openSession()來建立一個session,再將session set到線程中,實際是放到當前線程的ThreadLocalMap這個map中,這時,對於這個session的唯一引用就是當前線程中的那個ThreadLocalMap(下面會講到),而threadSession作為這個值的key,要取得這個session可以通過threadSession.get()來得到,裡面執行的操作實際是先取得當前線程中的ThreadLocalMap,然後將threadSession作為key將對應的值取出。這個session相當於線程的私人變數,而不是public的。
顯然,其他線程中是取不到這個session的,他們也只能取到自己的ThreadLocalMap中的東西。要是session是多個線程共用使用的,那還不亂套了。
試想如果不用ThreadLocal怎麼來實現呢?可能就要在action中建立session,然後把session一個個傳到service和dao中,這可夠麻煩的。或者可以自己定義一個靜態map,將當前thread作為key,建立的session作為值,put到map中,應該也行,這也是一般人的想法,但事實上,ThreadLocal的實現剛好相反,它是在每個線程中有一個map,而將ThreadLocal執行個體作為key,這樣每個map中的項數很少,而且當線程銷毀時相應的東西也一起銷毀了,不知道除了這些還有什麼其他的好處。
總之,ThreadLocal不是用來解決對象共用訪問問題的,而主要是提供了保持對象的方法和避免參數傳遞的方便的對象訪問方式。歸納了兩點:
1。每個線程中都有一個自己的ThreadLocalMap類對象,可以將線程自己的對象保持到其中,各管各的,線程可以正確的訪問到自己的對象。
2。將一個共用的ThreadLocal靜態執行個體作為key,將不同對象的引用儲存到不同線程的ThreadLocalMap中,然後線上程執行的各處通過這個靜態ThreadLocal執行個體的get()方法取得自己線程儲存的那個對象,避免了將這個對象作為參數傳遞的麻煩。
當然如果要把本來線程共用的對象通過ThreadLocal.set()放到線程中也可以,可以實現避免參數傳遞的訪問方式,但是要注意get()到的是那同一個共用對象,並發訪問問題要靠其他手段來解決。但一般來說線程共用的對象通過設定為某類的靜態變數就可以實現方便的訪問了,似乎沒必要放到線程中。
ThreadLocal的應用場合,我覺得最適合的是按線程多執行個體(每個線程對應一個執行個體)的對象的訪問,並且這個對象很多地方都要用到。