摘要:每個線程中都有一個自己的類對象,可以將線程自己的對象保持到其中,各管各的,線程可以正確的訪問到自己的對象。。但一般來說線程共享的對象通過設置為某類的靜態變量就可以實現方便的訪問了,似乎沒必要放到線程中。
一篇老文章,引用自:http://www.iteye.com/topic/103804
首先,ThreadLocal 不是用來解決共享對象的多線程訪問問題的,一般情況下,通過ThreadLocal.set() 到線程中的對象是該線程自己使用的對象,其他線程是不需要訪問的,也訪問不到的。各個線程中訪問的是不同的對象。
另外,說ThreadLocal使得各線程能夠保持各自獨立的一個對象,并不是通過ThreadLocal.set()來實現的,而是通過每個線程中的new 對象 的操作來創建的對象,每個線程創建一個,不是什么對象的拷貝或副本。通過ThreadLocal.set()將這個新創建的對象的引用保存到各線程的自己的一個map中,每個線程都有這樣一個map,執行ThreadLocal.get()時,各線程從自己的map中取出放進去的對象,因此取出來的是各自自己線程中的對象,ThreadLocal實例是作為map的key來使用的。
如果ThreadLocal.set()進去的東西本來就是多個線程共享的同一個對象,那么多個線程的ThreadLocal.get()取得的還是這個共享對象本身,還是有并發訪問問題。
下面來看一個hibernate中典型的ThreadLocal的應用:
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的應用場合,我覺得最適合的是按線程多實例(每個線程對應一個實例)的對象的訪問,并且這個對象很多地方都要用到。
下面來看看ThreadLocal的實現原理(jdk1.5源碼)
public class ThreadLocal{ /** * ThreadLocals rely on per-thread hash maps attached to each thread * (Thread.threadLocals and inheritableThreadLocals). The ThreadLocal * objects act as keys, searched via threadLocalHashCode. This is a * custom hash code (useful only within ThreadLocalMaps) that eliminates * collisions in the common case where consecutively constructed * ThreadLocals are used by the same threads, while remaining well-behaved * in less common cases. */ private final int threadLocalHashCode = nextHashCode(); /** * The next hash code to be given out. Accessed only by like-named method. */ private static int nextHashCode = 0; /** * The difference between successively generated hash codes - turns * implicit sequential thread-local IDs into near-optimally spread * multiplicative hash values for power-of-two-sized tables. */ private static final int HASH_INCREMENT = 0x61c88647; /** * Compute the next hash code. The static synchronization used here * should not be a performance bottleneck. When ThreadLocals are * generated in different threads at a fast enough rate to regularly * contend on this lock, memory contention is by far a more serious * problem than lock contention. */ private static synchronized int nextHashCode() { int h = nextHashCode; nextHashCode = h + HASH_INCREMENT; return h; } /** * Creates a thread local variable. */ public ThreadLocal() { } /** * Returns the value in the current thread"s copy of this thread-local * variable. Creates and initializes the copy if this is the first time * the thread has called this method. * * @return the current thread"s value of this thread-local */ public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) return (T)map.get(this); // Maps are constructed lazily. if the map for this thread // doesn"t exist, create it, with this ThreadLocal and its // initial value as its only entry. T value = initialValue(); createMap(t, value); return value; } /** * Sets the current thread"s copy of this thread-local variable * to the specified value. Many applications will have no need for * this functionality, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current threads" copy of * this thread-local. */ public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } /** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @return the map */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; } /** * Create the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @param firstValue value for the initial entry of the map * @param map the map to store. */ void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } ....... /** * ThreadLocalMap is a customized hash map suitable only for * maintaining thread local values. No operations are exported * outside of the ThreadLocal class. The class is package private to * allow declaration of fields in class Thread. To help deal with * very large and long-lived usages, the hash table entries use * WeakReferences for keys. However, since reference queues are not * used, stale entries are guaranteed to be removed only when * the table starts running out of space. */ static class ThreadLocalMap { ........ } }
可以看到ThreadLocal類中的變量只有這3個int型:
private final int threadLocalHashCode = nextHashCode(); private static int nextHashCode = 0; private static final int HASH_INCREMENT = 0x61c88647;
而作為ThreadLocal實例的變量只有 threadLocalHashCode 這一個,nextHashCode 和HASH_INCREMENT 是ThreadLocal類的靜態變量,實際上HASH_INCREMENT是一個常量,表示了連續分配的兩個ThreadLocal實例的threadLocalHashCode值的增量,而nextHashCode 的表示了即將分配的下一個ThreadLocal實例的threadLocalHashCode 的值。
可以來看一下創建一個ThreadLocal實例即new ThreadLocal()時做了哪些操作,從上面看到構造函數ThreadLocal()里什么操作都沒有,唯一的操作是這句:
private final int threadLocalHashCode = nextHashCode();
那么nextHashCode()做了什么呢:
private static synchronized int nextHashCode() { int h = nextHashCode; nextHashCode = h + HASH_INCREMENT; return h; }
就是將ThreadLocal類的下一個hashCode值即nextHashCode的值賦給實例的threadLocalHashCode,然后nextHashCode的值增加HASH_INCREMENT這個值。
因此ThreadLocal實例的變量只有這個threadLocalHashCode,而且是final的,用來區分不同的ThreadLocal實例,ThreadLocal類主要是作為工具類來使用,那么ThreadLocal.set()進去的對象是放在哪兒的呢?
看一下上面的set()方法,兩句合并一下成為
ThreadLocalMap map = Thread.currentThread().threadLocals;
這個ThreadLocalMap 類是ThreadLocal中定義的內部類,但是它的實例卻用在Thread類中:
public class Thread implements Runnable { ...... /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null; ...... }
再看這句:
if (map != null) map.set(this, value);
也就是將該ThreadLocal實例作為key,要保持的對象作為值,設置到當前線程的ThreadLocalMap 中,get()方法同樣大家看了代碼也就明白了,ThreadLocalMap 類的代碼太多了,我就不帖了,自己去看源碼吧。
寫了這么多,也不知講明白了沒有,有什么不當的地方還請大家指出來。
附原文地址
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/64163.html
摘要:將當前線程局部變量的值刪除。是調用當期線程,返回當前線程中的一個成員變量。的一個使用示例測試類啟動兩個線程,第一個線程中存儲的為,第二個線程中存儲的為。 ThreadLocal是什么? ThreadLocal 源碼解釋: This class provides thread-local variables. These variables differ from their norma...
摘要:基本原理線程本地變量是和線程相關的變量,一個線程則一份數據。其中為聲明的對象。對于一個對象倘若沒有成員變量,單例非常簡單,不用去擔心多線程同時對成員變量修改而產生的線程安全問題。并且還不能使用單例模式,因為是不能多線程訪問的。 ThreadLocal簡述 下面我們看一下ThreadLocal類的官方注釋。 This class provides thread-local variab...
摘要:并發設計模式一模式的使用表示線程本地存儲模式。為不同的任務創建不同的線程池,這樣能夠有效的避免死鎖問題。兩階段終止,即將線程的結束分為了兩個階段,第一個階段是一個線程向另一個線程發送終止指令,第二個階段是線程響應終止指令。 Java 并發設計模式 一、Thread Local Storage 模式 1. ThreadLocal 的使用 Thread Local Storage 表示線程...
摘要:讀了周勇老師的從零開始寫框架,感覺干貨還是挺多的。不過,這本書中的從零開始并不是指的零基礎,而是從無到有。還是先說說目前的感受吧。第五章講了的優化文件上傳和下載集成安全框架和框架。如果大家看了這本書有什么新的感悟,也歡迎分享給我。 讀了周勇老師的《從零開始寫javaweb框架》,感覺干貨還是挺多的。想把自己的收獲分享給大家。不過,這本書中的從零開始并不是指的零基礎,而是從無到有。所以,...
摘要:底層是一個的散列表可擴容的數組,并采用開放地址法來解決沖突。稍后討論方法每個對象都有一個值,每初始化一個對象,值就增加一個固定的大小。因此在使用的時候要手動調用方法,防止內存泄漏。 ThreadLocal定義 先看JDK關于ThreadLocal的類注釋: This class provides thread-local variables. These variables diffe...
閱讀 1977·2021-09-09 09:33
閱讀 1115·2019-08-30 15:43
閱讀 2665·2019-08-30 13:45
閱讀 3306·2019-08-29 11:00
閱讀 854·2019-08-26 14:01
閱讀 3570·2019-08-26 13:24
閱讀 480·2019-08-26 11:56
閱讀 2689·2019-08-26 10:27