吐槽:博客園的編輯器好爛啊,編輯了好屢次代碼都沒法插入,搞的亂七八糟的java
首先,ThreadLocal 不是用來解決共享對象的多線程訪問問題的,通常狀況下,經過ThreadLocal.set() 到線程中的對象是該線程本身使用的對象,其餘線程是不須要訪問的,也訪問不到的。各個線程中訪問的是不一樣的對象。
另外,說ThreadLocal使得各線程可以保持各自獨立的一個對象,並非經過ThreadLocal.set()來實現的,而是經過每一個線程中的new 對象 的操做來建立的對象,每一個線程建立一個,不是什麼對象的拷貝或副本。經過ThreadLocal.set()將這個新建立的對象的引用保存到各線程的本身的一個map中,每一個線程都有這樣一個map,執行ThreadLocal.get()時,各線程從本身的map中取出放進去的對象,所以取出來的是各自本身線程中的對象,ThreadLocal實例是做爲map的key來使用的。
若是ThreadLocal.set()進去的東西原本就是多個線程共享的同一個對象,那麼多個線程的ThreadLocal.get()取得的仍是這個共享對象自己,仍是有併發訪問問題。
下面來看一個hibernate中典型的ThreadLocal的應用:session
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做爲這個threadlocal變量的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()到的是那同一個共享對象,併發訪問問題要靠其餘手段來解決。但通常來講線程共享的對象經過設置爲某類的靜態變量就能夠實現方便的訪問了,彷佛不必放到線程中。 app
ThreadLocal的應用場合,我以爲最適合的是按線程多實例(每一個線程對應一個實例)的對象的訪問,而且這個對象不少地方都要用到。
下面來看看ThreadLocal的實現原理(jdk1.5源碼) less
1 public class ThreadLocal<T> { 2 /** 3 * ThreadLocals rely on per-thread hash maps attached to each thread 4 * (Thread.threadLocals and inheritableThreadLocals). The ThreadLocal 5 * objects act as keys, searched via threadLocalHashCode. This is a 6 * custom hash code (useful only within ThreadLocalMaps) that eliminates 7 * collisions in the common case where consecutively constructed 8 * ThreadLocals are used by the same threads, while remaining well-behaved 9 * in less common cases. 10 */ 11 private final int threadLocalHashCode = nextHashCode(); 12 13 /** 14 * The next hash code to be given out. Accessed only by like-named method. 15 */ 16 private static int nextHashCode = 0; 17 18 /** 19 * The difference between successively generated hash codes - turns 20 * implicit sequential thread-local IDs into near-optimally spread 21 * multiplicative hash values for power-of-two-sized tables. 22 */ 23 private static final int HASH_INCREMENT = 0x61c88647; 24 25 /** 26 * Compute the next hash code. The static synchronization used here 27 * should not be a performance bottleneck. When ThreadLocals are 28 * generated in different threads at a fast enough rate to regularly 29 * contend on this lock, memory contention is by far a more serious 30 * problem than lock contention. 31 */ 32 private static synchronized int nextHashCode() { 33 int h = nextHashCode; 34 nextHashCode = h + HASH_INCREMENT; 35 return h; 36 } 37 38 /** 39 * Creates a thread local variable. 40 */ 41 public ThreadLocal() { 42 } 43 44 /** 45 * Returns the value in the current thread's copy of this thread-local 46 * variable. Creates and initializes the copy if this is the first time 47 * the thread has called this method. 48 * 49 * @return the current thread's value of this thread-local 50 */ 51 public T get() { 52 Thread t = Thread.currentThread(); 53 ThreadLocalMap map = getMap(t); 54 if (map != null) 55 return (T)map.get(this); 56 57 // Maps are constructed lazily. if the map for this thread 58 // doesn't exist, create it, with this ThreadLocal and its 59 // initial value as its only entry. 60 T value = initialValue(); 61 createMap(t, value); 62 return value; 63 } 64 65 /** 66 * Sets the current thread's copy of this thread-local variable 67 * to the specified value. Many applications will have no need for 68 * this functionality, relying solely on the {@link #initialValue} 69 * method to set the values of thread-locals. 70 * 71 * @param value the value to be stored in the current threads' copy of 72 * this thread-local. 73 */ 74 public void set(T value) { 75 Thread t = Thread.currentThread(); 76 ThreadLocalMap map = getMap(t); 77 if (map != null) 78 map.set(this, value); 79 else 80 createMap(t, value); 81 } 82 83 /** 84 * Get the map associated with a ThreadLocal. Overridden in 85 * InheritableThreadLocal. 86 * 87 * @param t the current thread 88 * @return the map 89 */ 90 ThreadLocalMap getMap(Thread t) { 91 return t.threadLocals; 92 } 93 94 /** 95 * Create the map associated with a ThreadLocal. Overridden in 96 * InheritableThreadLocal. 97 * 98 * @param t the current thread 99 * @param firstValue value for the initial entry of the map 100 * @param map the map to store. 101 */ 102 void createMap(Thread t, T firstValue) { 103 t.threadLocals = new ThreadLocalMap(this, firstValue); 104 } 105 106 ....... 107 108 /** 109 * ThreadLocalMap is a customized hash map suitable only for 110 * maintaining thread local values. No operations are exported 111 * outside of the ThreadLocal class. The class is package private to 112 * allow declaration of fields in class Thread. To help deal with 113 * very large and long-lived usages, the hash table entries use 114 * WeakReferences for keys. However, since reference queues are not 115 * used, stale entries are guaranteed to be removed only when 116 * the table starts running out of space. 117 */ 118 static class ThreadLocalMap { 119 120 ........ 121 122 } 123 124 }
能夠看到ThreadLocal類中的變量只有這3個int型: 編輯器
private final int threadLocalHashCode = nextHashCode(); private static int nextHashCode = 0; private static final int HASH_INCREMENT = 0x61c88647;
(JDK1.6中,nextHashCode採用的是AtomicInteger)ide
/** * The next hash code to be given out. Updated atomically. Starts at * zero. */ private static AtomicInteger nextHashCode = new AtomicInteger();
而做爲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實例的變量只有這個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 類的代碼太多了,我就不帖了,本身去看源碼吧。
概括點2:
將一個共用的ThreadLocal靜態實例做爲key,將不一樣對象的引用保存到不一樣線程的ThreadLocalMap中,而後在線程執行的各處經過這個靜態ThreadLocal實例的get()方法取得本身線程保存的那個對象,避免了將這個對象做爲參數傳遞的麻煩。
是很是好用的。
項目中看到的代碼:
public void run() { try { // 清除客戶信息 ClientAuthUtil.setCurrentUserId(null); downloadTask = getTask(); if (downloadTask == null) { logger.info(threadLogHead + "未得到須要處理的任務"); return; } // 設置認證客戶信息 ClientAuthUtil.setCurrentUserId(downloadTask.getAccountId().toString());//紅色字體爲threadlocal的value,每一個線程都有本身的value ....... }
public class ClientAuthUtil { private static ThreadLocal<String> currentUserId = new ThreadLocal<String>(); public static String getCurrentUserId() { return currentUserId.get(); } public static void setCurrentUserId(String currentUserIdValue) { ClientAuthUtil.currentUserId.set(currentUserIdValue);//ThreadLocalMap中的key統一爲紅色字體currentUserId,每一個線程的value爲本身的currentUserIdValue } }
public class ThreadLocal<T> { public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); } private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; } }
仔細想來,這裏的threadlocal用的的確是很漂亮,用一個共享的Threadlocal靜態屬性作key,即節省空間,又方便統一維護。將不一樣對象的引用保存到ThreadlocalMap中,而線程取本身保存的對象時,也只須要調用CurrentAuthUtil.getCurrentUserId()便可。CurrentAuthUtil就像管家同樣,維護着全部線程的currentUserId,每當一個線程想取本身的value時,直接告訴CurrentAuthUtil便可。避免了將這個對象做爲參數傳遞的麻煩。
博文轉載整理自:http://www.iteye.com/topic/103804