java之ThreadLocal筆記

wzm10455發表於2013-06-17

源地址;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程式碼:
  1. private static final ThreadLocal threadSession = new ThreadLocal();  
  2.   
  3. public static Session getSession() throws InfrastructureException {  
  4.     Session s = (Session) threadSession.get();  
  5.     try {  
  6.         if (s == null) {  
  7.             s = getSessionFactory().openSession();  
  8.             threadSession.set(s);  
  9.         }  
  10.     } catch (HibernateException ex) {  
  11.         throw new InfrastructureException(ex);  
  12.     }  
  13.     return s;  
  14. }  
可以看到在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的應用場合,我覺得最適合的是按執行緒多例項(每個執行緒對應一個例項)的物件的訪問,並且這個物件很多地方都要用到。

相關文章