1、前言java
要理解ThreadLocal,首先必須理解線程安全。線程能夠看作是一個具備必定獨立功能的處理過程,它是比進程更細度的單位。當程序以單線程運行的時候,咱們不須要考慮線程安全。然而當一個進程中包含多個線程的時候,就須要考慮線程安全問題,由於此時線程可能會同時操做同一個資源,當兩個或者兩個以上線程同時操做一個資源的時候,就會形成衝突、不一致等問題,即線程不安全。數組
解決線程安全問題,本質上就是解決資源共享問題,通常有如下手段:安全
1)可重入(不依賴環境);2)互斥(同一時間段只容許一個線程使用);3)原子操做;4)Thread-Local多線程
2、Thread-Local併發
Thread-Local是一個很簡單的思想:若是一個資源會引發線程競爭,那就爲每個線程配備一個資源。這就是ThreadLocal須要作的事情。less
3、ThreadLocal的用法dom
在理解ThreadLocal以前,首先看一下它的用法:ide
1 public class ThreadLocalTest { 2 public static ThreadLocal<Integer> intLocal = new ThreadLocal<Integer>() { 3 @Override 4 protected Integer initialValue() { 5 // TODO Auto-generated method stub 6 return 0; 7 } 8 9 @Override 10 public Integer get() { 11 // TODO Auto-generated method stub 12 set(super.get() + 1); 13 return super.get(); 14 } 15 16 @Override 17 public void set(Integer value) { 18 // TODO Auto-generated method stub 19 super.set(value); 20 } 21 22 @Override 23 public void remove() { 24 // TODO Auto-generated method stub 25 super.remove(); 26 } 27 }; 28 29 public static void main(String[] args) { 30 // TODO Auto-generated method stub 31 for(int index = 0; index < 3; index ++) 32 new MyThread(index).start(); 33 } 34 } 35 36 class MyThread extends Thread{ 37 int id; 38 39 public MyThread(int id){ 40 this.id = id; 41 } 42 43 @Override 44 public void run() { 45 // TODO Auto-generated method stub 46 for(int index = 0; index < 3; index ++){ 47 System.out.println("Thread-" + id + " : " + ThreadLocalTest.intLocal.get()); 48 try { 49 Thread.sleep((int)(100 * Math.random())); 50 } catch (InterruptedException e) { 51 // TODO Auto-generated catch block 52 e.printStackTrace(); 53 } 54 } 55 } 56 }
其打印結果以下:學習
1 Thread-1 : 1 2 Thread-0 : 1 3 Thread-2 : 1 4 Thread-0 : 2 5 Thread-2 : 2 6 Thread-1 : 2 7 Thread-2 : 3 8 Thread-0 : 3 9 Thread-1 : 3
這是一個很典型的問題,在學習多線程以及同步的時候,幾乎全部的書本都會使用相似的一個例子:銀行存錢取錢問題。咱們知道當多線程併發操做一個int值的加減操做的時候,最後的數值會產生很大的不肯定性,得不到最終正確的結果。ui
而從例子中咱們能夠看到:每個線程對int值的操做都是獨立的,咱們使用的只是同一個靜態的intLocal類型!經過使用TreadLocal,咱們能夠爲每個線程提供獨立的資源副本,從而完成對資源的「共享」操做。
ThreadLocal類中可重載的方法只有四個:
1)set():設置值,也就是說,咱們選擇將某個值設置爲ThreadLocal類型的;
2)get():將設置進去的值取出來;
3)remove():咱們不想將某個值設置爲ThreadLocal了,移除掉;
4)initialValue():若是get的時候尚未設置值,就使用這個方法進行初始化;
使用過程簡單明瞭,通常重載initialValue()提供一個初始值就能夠了,其他方法不須要重載。
4、ThreadLocal的實現
看源碼是最直接也是最有效的學習方式,不但能夠掌握其原理,也能夠學習Java源碼精巧的實現方式。
ThreadLocal的實現代碼略長,咱們選取重要的方法做爲切入點:
1 public T get() { 2 Thread t = Thread.currentThread(); 3 ThreadLocalMap map = getMap(t); 4 if (map != null) { 5 ThreadLocalMap.Entry e = map.getEntry(this); 6 if (e != null) 7 return (T)e.value; 8 } 9 return setInitialValue(); 10 }
第一個方法是get()方法。這邊出現了Thread和Map,聯想到Thread的做用,大體應該能夠猜到ThreadLocal的實現:維護一個Map,以Thread做爲鍵,以變量做爲值,每一次要使用變量的時候,就以當前的Thread做爲鍵去取值,若是沒有,就初始化一個值返回,若是有,就直接返回。
事實上,ThreadLocal的實現思路的確大體如此。可是咱們要作的事情其實更多:
A.若是要設置更多的值怎麼辦?也就是說,咱們有多種資源須要共享怎麼辦?
B.爲每個線程共享一個資源,如何回收?
咱們言歸正傳,從源碼中獲取答案。
get()方法的第3行中出現了一個ThreadLocalMap實例,它是從getMap()方法獲取的,其方法以下:
1 ThreadLocalMap getMap(Thread t) { 2 return t.threadLocals; 3 }
t表示當前的線程,從Thread的源碼中能夠看到,的確是有一個ThreadLocalMap實例,其聲明和註解以下:
1 /* ThreadLocal values pertaining to this thread. This map is maintained 2 * by the ThreadLocal class. */ 3 ThreadLocal.ThreadLocalMap threadLocals = null;
這個屬性是專門爲ThreadLocal類而存在的,而它的實現也存在於ThreadLocal中,是ThreadLocal的一個靜態內部類。其類註釋以下:
1 /** 2 * ThreadLocalMap is a customized hash map suitable only for 3 * maintaining thread local values. No operations are exported 4 * outside of the ThreadLocal class. The class is package private to 5 * allow declaration of fields in class Thread. To help deal with 6 * very large and long-lived usages, the hash table entries use 7 * WeakReferences for keys. However, since reference queues are not 8 * used, stale entries are guaranteed to be removed only when 9 * the table starts running out of space. 10 */
它是一個定製的HashMap(天然具備HashMap的相關特性,好比自動擴增容量等)。
前面提到A,B兩個問題,從源碼來看,A問題的解決方案就是,爲每個Thread維護一個HashMap,在這裏就是維護一個ThreadLocalMap屬性,這個屬性的鍵是ThreadLocal,值就是資源副本,詳細描述以下:
每個Thread都有一個ThreadLocalMap屬性,這個屬性是相似於HashMap的,它以ThreadLocal爲鍵,以屬於該線程的資源副本爲值。咱們能夠這樣看待ThreadLocal:ThreadLocal是爲一組線程維護資源副本的對象,經過它,能夠爲每個線程建立資源副本,也能夠正確得到屬於某一線程的資源副本。
每個ThreadLocal只能維護一個共享資源,一旦聲明ThreadLocal實例,線程在調用的其get()方法獲取資源副本的時候,就能夠自動設置綁定到該線程自己。
好,如今轉了一小圈回到get方法()。get()方法的第二、3行很明顯是獲取屬於當前線程的ThreadLocalMap,若是這個map不爲空,咱們就以當前的ThreadLocal爲鍵,去獲取相應的Entry,Entry是ThreadLocalMap的靜態內部類,其定義以下:
1 static class Entry extends WeakReference<ThreadLocal> { 2 /** The value associated with this ThreadLocal. */ 3 Object value; 4 5 Entry(ThreadLocal k, Object v) { 6 super(k); 7 value = v; 8 } 9 }
它繼承與弱引用,因此在get()方法裏面如第7行同樣調用e.value方法就能夠獲取實際的資源副本值。可是若是有一個爲空,說明屬於該線程的資源副本還不存在,則須要去建立資源副本,從代碼中能夠看到是調用setInitialValue()方法,其定義以下:
1 /** 2 * Variant of set() to establish initialValue. Used instead 3 * of set() in case user has overridden the set() method. 4 * 5 * @return the initial value 6 */ 7 private T setInitialValue() { 8 T value = initialValue(); 9 Thread t = Thread.currentThread(); 10 ThreadLocalMap map = getMap(t); 11 if (map != null) 12 map.set(this, value); 13 else 14 createMap(t, value); 15 return value; 16 }
第8行調用initialValue()方法初始化一個值,還記得在一開始的例子中,咱們重載這個方法產生一個初始化的值麼?
接下來是判斷線程的ThreadLocalMap是否爲空,不爲空就直接這是值(鍵爲this,值爲value),爲空則建立一個Map,調用方法爲createMap(),其定義以下:
1 void createMap(Thread t, T firstValue) { 2 t.threadLocals = new ThreadLocalMap(this, firstValue); 3 }
簡單明瞭,而ThreadLocalMap的這個構造方法的實現以下:
/** * Construct a new map initially containing (firstKey, firstValue). * ThreadLocalMaps are constructed lazily, so we only create * one when we have at least one entry to put in it. */ ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); }
實例化table數組用於存儲鍵值對,而後經過映射將鍵值對存儲進入相應的位置。
至於set方法,看完get()後應該很簡單了,本身均可以實現:
1 /** 2 * Sets the current thread's copy of this thread-local variable 3 * to the specified value. Most subclasses will have no need to 4 * override this method, relying solely on the {@link #initialValue} 5 * method to set the values of thread-locals. 6 * 7 * @param value the value to be stored in the current thread's copy of 8 * this thread-local. 9 */ 10 public void set(T value) { 11 Thread t = Thread.currentThread(); 12 ThreadLocalMap map = getMap(t); 13 if (map != null) 14 map.set(this, value); 15 else 16 createMap(t, value); 17 }
如今還剩一個問題B,會形成內存泄露嗎?ThreadLocal的類註釋裏面有一段話:
/* Each thread holds an implicit reference to its copy of a thread-local * variable as long as the thread is alive and the <tt>ThreadLocal</tt> * instance is accessible; after a thread goes away, all of its copies of * thread-local instances are subject to garbage collection (unless other * references to these copies exist). */
每個線程對資源副本都有一個隱式引用:只要線程還在運行,只要ThreadLocal仍是能夠獲取的。當一個線程運行結束銷燬時,全部的資源副本都是能夠被垃圾回收的。這段註釋代表,ThreadLocal的使用是不會形成內訓泄露的。
可是咱們來仔細分析一下,我想畫一張Thread、ThreadLocal和ThreadLocalMap的依賴關係圖,可是在繪製過程當中,我發現:根本就沒有依賴!
很是驚訝,我在這裏把ThreadLocal完整的源碼貼一遍,讀者能夠自行審視。
1 package java.lang; 2 import java.lang.ref.*; 3 import java.util.concurrent.atomic.AtomicInteger; 4 5 /** 6 * This class provides thread-local variables. These variables differ from 7 * their normal counterparts in that each thread that accesses one (via its 8 * <tt>get</tt> or <tt>set</tt> method) has its own, independently initialized 9 * copy of the variable. <tt>ThreadLocal</tt> instances are typically private 10 * static fields in classes that wish to associate state with a thread (e.g., 11 * a user ID or Transaction ID). 12 * 13 * <p>For example, the class below generates unique identifiers local to each 14 * thread. 15 * A thread's id is assigned the first time it invokes <tt>ThreadId.get()</tt> 16 * and remains unchanged on subsequent calls. 17 * <pre> 18 * import java.util.concurrent.atomic.AtomicInteger; 19 * 20 * public class ThreadId { 21 * // Atomic integer containing the next thread ID to be assigned 22 * private static final AtomicInteger nextId = new AtomicInteger(0); 23 * 24 * // Thread local variable containing each thread's ID 25 * private static final ThreadLocal<Integer> threadId = 26 * new ThreadLocal<Integer>() { 27 * @Override protected Integer initialValue() { 28 * return nextId.getAndIncrement(); 29 * } 30 * }; 31 * 32 * // Returns the current thread's unique ID, assigning it if necessary 33 * public static int get() { 34 * return threadId.get(); 35 * } 36 * } 37 * </pre> 38 * <p>Each thread holds an implicit reference to its copy of a thread-local 39 * variable as long as the thread is alive and the <tt>ThreadLocal</tt> 40 * instance is accessible; after a thread goes away, all of its copies of 41 * thread-local instances are subject to garbage collection (unless other 42 * references to these copies exist). 43 * 44 * @author Josh Bloch and Doug Lea 45 * @since 1.2 46 */ 47 public class ThreadLocal<T> { 48 /** 49 * ThreadLocals rely on per-thread linear-probe hash maps attached 50 * to each thread (Thread.threadLocals and 51 * inheritableThreadLocals). The ThreadLocal objects act as keys, 52 * searched via threadLocalHashCode. This is a custom hash code 53 * (useful only within ThreadLocalMaps) that eliminates collisions 54 * in the common case where consecutively constructed ThreadLocals 55 * are used by the same threads, while remaining well-behaved in 56 * less common cases. 57 */ 58 private final int threadLocalHashCode = nextHashCode(); 59 60 /** 61 * The next hash code to be given out. Updated atomically. Starts at 62 * zero. 63 */ 64 private static AtomicInteger nextHashCode = 65 new AtomicInteger(); 66 67 /** 68 * The difference between successively generated hash codes - turns 69 * implicit sequential thread-local IDs into near-optimally spread 70 * multiplicative hash values for power-of-two-sized tables. 71 */ 72 private static final int HASH_INCREMENT = 0x61c88647; 73 74 /** 75 * Returns the next hash code. 76 */ 77 private static int nextHashCode() { 78 return nextHashCode.getAndAdd(HASH_INCREMENT); 79 } 80 81 /** 82 * Returns the current thread's "initial value" for this 83 * thread-local variable. This method will be invoked the first 84 * time a thread accesses the variable with the {@link #get} 85 * method, unless the thread previously invoked the {@link #set} 86 * method, in which case the <tt>initialValue</tt> method will not 87 * be invoked for the thread. Normally, this method is invoked at 88 * most once per thread, but it may be invoked again in case of 89 * subsequent invocations of {@link #remove} followed by {@link #get}. 90 * 91 * <p>This implementation simply returns <tt>null</tt>; if the 92 * programmer desires thread-local variables to have an initial 93 * value other than <tt>null</tt>, <tt>ThreadLocal</tt> must be 94 * subclassed, and this method overridden. Typically, an 95 * anonymous inner class will be used. 96 * 97 * @return the initial value for this thread-local 98 */ 99 protected T initialValue() { 100 return null; 101 } 102 103 /** 104 * Creates a thread local variable. 105 */ 106 public ThreadLocal() { 107 } 108 109 /** 110 * Returns the value in the current thread's copy of this 111 * thread-local variable. If the variable has no value for the 112 * current thread, it is first initialized to the value returned 113 * by an invocation of the {@link #initialValue} method. 114 * 115 * @return the current thread's value of this thread-local 116 */ 117 public T get() { 118 Thread t = Thread.currentThread(); 119 ThreadLocalMap map = getMap(t); 120 if (map != null) { 121 ThreadLocalMap.Entry e = map.getEntry(this); 122 if (e != null) 123 return (T)e.value; 124 } 125 return setInitialValue(); 126 } 127 128 /** 129 * Variant of set() to establish initialValue. Used instead 130 * of set() in case user has overridden the set() method. 131 * 132 * @return the initial value 133 */ 134 private T setInitialValue() { 135 T value = initialValue(); 136 Thread t = Thread.currentThread(); 137 ThreadLocalMap map = getMap(t); 138 if (map != null) 139 map.set(this, value); 140 else 141 createMap(t, value); 142 return value; 143 } 144 145 /** 146 * Sets the current thread's copy of this thread-local variable 147 * to the specified value. Most subclasses will have no need to 148 * override this method, relying solely on the {@link #initialValue} 149 * method to set the values of thread-locals. 150 * 151 * @param value the value to be stored in the current thread's copy of 152 * this thread-local. 153 */ 154 public void set(T value) { 155 Thread t = Thread.currentThread(); 156 ThreadLocalMap map = getMap(t); 157 if (map != null) 158 map.set(this, value); 159 else 160 createMap(t, value); 161 } 162 163 /** 164 * Removes the current thread's value for this thread-local 165 * variable. If this thread-local variable is subsequently 166 * {@linkplain #get read} by the current thread, its value will be 167 * reinitialized by invoking its {@link #initialValue} method, 168 * unless its value is {@linkplain #set set} by the current thread 169 * in the interim. This may result in multiple invocations of the 170 * <tt>initialValue</tt> method in the current thread. 171 * 172 * @since 1.5 173 */ 174 public void remove() { 175 ThreadLocalMap m = getMap(Thread.currentThread()); 176 if (m != null) 177 m.remove(this); 178 } 179 180 /** 181 * Get the map associated with a ThreadLocal. Overridden in 182 * InheritableThreadLocal. 183 * 184 * @param t the current thread 185 * @return the map 186 */ 187 ThreadLocalMap getMap(Thread t) { 188 return t.threadLocals; 189 } 190 191 /** 192 * Create the map associated with a ThreadLocal. Overridden in 193 * InheritableThreadLocal. 194 * 195 * @param t the current thread 196 * @param firstValue value for the initial entry of the map 197 * @param map the map to store. 198 */ 199 void createMap(Thread t, T firstValue) { 200 t.threadLocals = new ThreadLocalMap(this, firstValue); 201 } 202 203 /** 204 * Factory method to create map of inherited thread locals. 205 * Designed to be called only from Thread constructor. 206 * 207 * @param parentMap the map associated with parent thread 208 * @return a map containing the parent's inheritable bindings 209 */ 210 static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) { 211 return new ThreadLocalMap(parentMap); 212 } 213 214 /** 215 * Method childValue is visibly defined in subclass 216 * InheritableThreadLocal, but is internally defined here for the 217 * sake of providing createInheritedMap factory method without 218 * needing to subclass the map class in InheritableThreadLocal. 219 * This technique is preferable to the alternative of embedding 220 * instanceof tests in methods. 221 */ 222 T childValue(T parentValue) { 223 throw new UnsupportedOperationException(); 224 } 225 226 /** 227 * ThreadLocalMap is a customized hash map suitable only for 228 * maintaining thread local values. No operations are exported 229 * outside of the ThreadLocal class. The class is package private to 230 * allow declaration of fields in class Thread. To help deal with 231 * very large and long-lived usages, the hash table entries use 232 * WeakReferences for keys. However, since reference queues are not 233 * used, stale entries are guaranteed to be removed only when 234 * the table starts running out of space. 235 */ 236 static class ThreadLocalMap { 237 238 /** 239 * The entries in this hash map extend WeakReference, using 240 * its main ref field as the key (which is always a 241 * ThreadLocal object). Note that null keys (i.e. entry.get() 242 * == null) mean that the key is no longer referenced, so the 243 * entry can be expunged from table. Such entries are referred to 244 * as "stale entries" in the code that follows. 245 */ 246 static class Entry extends WeakReference<ThreadLocal> { 247 /** The value associated with this ThreadLocal. */ 248 Object value; 249 250 Entry(ThreadLocal k, Object v) { 251 super(k); 252 value = v; 253 } 254 } 255 256 /** 257 * The initial capacity -- MUST be a power of two. 258 */ 259 private static final int INITIAL_CAPACITY = 16; 260 261 /** 262 * The table, resized as necessary. 263 * table.length MUST always be a power of two. 264 */ 265 private Entry[] table; 266 267 /** 268 * The number of entries in the table. 269 */ 270 private int size = 0; 271 272 /** 273 * The next size value at which to resize. 274 */ 275 private int threshold; // Default to 0 276 277 /** 278 * Set the resize threshold to maintain at worst a 2/3 load factor. 279 */ 280 private void setThreshold(int len) { 281 threshold = len * 2 / 3; 282 } 283 284 /** 285 * Increment i modulo len. 286 */ 287 private static int nextIndex(int i, int len) { 288 return ((i + 1 < len) ? i + 1 : 0); 289 } 290 291 /** 292 * Decrement i modulo len. 293 */ 294 private static int prevIndex(int i, int len) { 295 return ((i - 1 >= 0) ? i - 1 : len - 1); 296 } 297 298 /** 299 * Construct a new map initially containing (firstKey, firstValue). 300 * ThreadLocalMaps are constructed lazily, so we only create 301 * one when we have at least one entry to put in it. 302 */ 303 ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { 304 table = new Entry[INITIAL_CAPACITY]; 305 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); 306 table[i] = new Entry(firstKey, firstValue); 307 size = 1; 308 setThreshold(INITIAL_CAPACITY); 309 } 310 311 /** 312 * Construct a new map including all Inheritable ThreadLocals 313 * from given parent map. Called only by createInheritedMap. 314 * 315 * @param parentMap the map associated with parent thread. 316 */ 317 private ThreadLocalMap(ThreadLocalMap parentMap) { 318 Entry[] parentTable = parentMap.table; 319 int len = parentTable.length; 320 setThreshold(len); 321 table = new Entry[len]; 322 323 for (int j = 0; j < len; j++) { 324 Entry e = parentTable[j]; 325 if (e != null) { 326 ThreadLocal key = e.get(); 327 if (key != null) { 328 Object value = key.childValue(e.value); 329 Entry c = new Entry(key, value); 330 int h = key.threadLocalHashCode & (len - 1); 331 while (table[h] != null) 332 h = nextIndex(h, len); 333 table[h] = c; 334 size++; 335 } 336 } 337 } 338 } 339 340 /** 341 * Get the entry associated with key. This method 342 * itself handles only the fast path: a direct hit of existing 343 * key. It otherwise relays to getEntryAfterMiss. This is 344 * designed to maximize performance for direct hits, in part 345 * by making this method readily inlinable. 346 * 347 * @param key the thread local object 348 * @return the entry associated with key, or null if no such 349 */ 350 private Entry getEntry(ThreadLocal key) { 351 int i = key.threadLocalHashCode & (table.length - 1); 352 Entry e = table[i]; 353 if (e != null && e.get() == key) 354 return e; 355 else 356 return getEntryAfterMiss(key, i, e); 357 } 358 359 /** 360 * Version of getEntry method for use when key is not found in 361 * its direct hash slot. 362 * 363 * @param key the thread local object 364 * @param i the table index for key's hash code 365 * @param e the entry at table[i] 366 * @return the entry associated with key, or null if no such 367 */ 368 private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) { 369 Entry[] tab = table; 370 int len = tab.length; 371 372 while (e != null) { 373 ThreadLocal k = e.get(); 374 if (k == key) 375 return e; 376 if (k == null) 377 expungeStaleEntry(i); 378 else 379 i = nextIndex(i, len); 380 e = tab[i]; 381 } 382 return null; 383 } 384 385 /** 386 * Set the value associated with key. 387 * 388 * @param key the thread local object 389 * @param value the value to be set 390 */ 391 private void set(ThreadLocal key, Object value) { 392 393 // We don't use a fast path as with get() because it is at 394 // least as common to use set() to create new entries as 395 // it is to replace existing ones, in which case, a fast 396 // path would fail more often than not. 397 398 Entry[] tab = table; 399 int len = tab.length; 400 int i = key.threadLocalHashCode & (len-1); 401 402 for (Entry e = tab[i]; 403 e != null; 404 e = tab[i = nextIndex(i, len)]) { 405 ThreadLocal k = e.get(); 406 407 if (k == key) { 408 e.value = value; 409 return; 410 } 411 412 if (k == null) { 413 replaceStaleEntry(key, value, i); 414 return; 415 } 416 } 417 418 tab[i] = new Entry(key, value); 419 int sz = ++size; 420 if (!cleanSomeSlots(i, sz) && sz >= threshold) 421 rehash(); 422 } 423 424 /** 425 * Remove the entry for key. 426 */ 427 private void remove(ThreadLocal key) { 428 Entry[] tab = table; 429 int len = tab.length; 430 int i = key.threadLocalHashCode & (len-1); 431 for (Entry e = tab[i]; 432 e != null; 433 e = tab[i = nextIndex(i, len)]) { 434 if (e.get() == key) { 435 e.clear(); 436 expungeStaleEntry(i); 437 return; 438 } 439 } 440 } 441 442 /** 443 * Replace a stale entry encountered during a set operation 444 * with an entry for the specified key. The value passed in 445 * the value parameter is stored in the entry, whether or not 446 * an entry already exists for the specified key. 447 * 448 * As a side effect, this method expunges all stale entries in the 449 * "run" containing the stale entry. (A run is a sequence of entries 450 * between two null slots.) 451 * 452 * @param key the key 453 * @param value the value to be associated with key 454 * @param staleSlot index of the first stale entry encountered while 455 * searching for key. 456 */ 457 private void replaceStaleEntry(ThreadLocal key, Object value, 458 int staleSlot) { 459 Entry[] tab = table; 460 int len = tab.length; 461 Entry e; 462 463 // Back up to check for prior stale entry in current run. 464 // We clean out whole runs at a time to avoid continual 465 // incremental rehashing due to garbage collector freeing 466 // up refs in bunches (i.e., whenever the collector runs). 467 int slotToExpunge = staleSlot; 468 for (int i = prevIndex(staleSlot, len); 469 (e = tab[i]) != null; 470 i = prevIndex(i, len)) 471 if (e.get() == null) 472 slotToExpunge = i; 473 474 // Find either the key or trailing null slot of run, whichever 475 // occurs first 476 for (int i = nextIndex(staleSlot, len); 477 (e = tab[i]) != null; 478 i = nextIndex(i, len)) { 479 ThreadLocal k = e.get(); 480 481 // If we find key, then we need to swap it 482 // with the stale entry to maintain hash table order. 483 // The newly stale slot, or any other stale slot 484 // encountered above it, can then be sent to expungeStaleEntry 485 // to remove or rehash all of the other entries in run. 486 if (k == key) { 487 e.value = value; 488 489 tab[i] = tab[staleSlot]; 490 tab[staleSlot] = e; 491 492 // Start expunge at preceding stale entry if it exists 493 if (slotToExpunge == staleSlot) 494 slotToExpunge = i; 495 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); 496 return; 497 } 498 499 // If we didn't find stale entry on backward scan, the 500 // first stale entry seen while scanning for key is the 501 // first still present in the run. 502 if (k == null && slotToExpunge == staleSlot) 503 slotToExpunge = i; 504 } 505 506 // If key not found, put new entry in stale slot 507 tab[staleSlot].value = null; 508 tab[staleSlot] = new Entry(key, value); 509 510 // If there are any other stale entries in run, expunge them 511 if (slotToExpunge != staleSlot) 512 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); 513 } 514 515 /** 516 * Expunge a stale entry by rehashing any possibly colliding entries 517 * lying between staleSlot and the next null slot. This also expunges 518 * any other stale entries encountered before the trailing null. See 519 * Knuth, Section 6.4 520 * 521 * @param staleSlot index of slot known to have null key 522 * @return the index of the next null slot after staleSlot 523 * (all between staleSlot and this slot will have been checked 524 * for expunging). 525 */ 526 private int expungeStaleEntry(int staleSlot) { 527 Entry[] tab = table; 528 int len = tab.length; 529 530 // expunge entry at staleSlot 531 tab[staleSlot].value = null; 532 tab[staleSlot] = null; 533 size--; 534 535 // Rehash until we encounter null 536 Entry e; 537 int i; 538 for (i = nextIndex(staleSlot, len); 539 (e = tab[i]) != null; 540 i = nextIndex(i, len)) { 541 ThreadLocal k = e.get(); 542 if (k == null) { 543 e.value = null; 544 tab[i] = null; 545 size--; 546 } else { 547 int h = k.threadLocalHashCode & (len - 1); 548 if (h != i) { 549 tab[i] = null; 550 551 // Unlike Knuth 6.4 Algorithm R, we must scan until 552 // null because multiple entries could have been stale. 553 while (tab[h] != null) 554 h = nextIndex(h, len); 555 tab[h] = e; 556 } 557 } 558 } 559 return i; 560 } 561 562 /** 563 * Heuristically scan some cells looking for stale entries. 564 * This is invoked when either a new element is added, or 565 * another stale one has been expunged. It performs a 566 * logarithmic number of scans, as a balance between no 567 * scanning (fast but retains garbage) and a number of scans 568 * proportional to number of elements, that would find all 569 * garbage but would cause some insertions to take O(n) time. 570 * 571 * @param i a position known NOT to hold a stale entry. The 572 * scan starts at the element after i. 573 * 574 * @param n scan control: <tt>log2(n)</tt> cells are scanned, 575 * unless a stale entry is found, in which case 576 * <tt>log2(table.length)-1</tt> additional cells are scanned. 577 * When called from insertions, this parameter is the number 578 * of elements, but when from replaceStaleEntry, it is the 579 * table length. (Note: all this could be changed to be either 580 * more or less aggressive by weighting n instead of just 581 * using straight log n. But this version is simple, fast, and 582 * seems to work well.) 583 * 584 * @return true if any stale entries have been removed. 585 */ 586 private boolean cleanSomeSlots(int i, int n) { 587 boolean removed = false; 588 Entry[] tab = table; 589 int len = tab.length; 590 do { 591 i = nextIndex(i, len); 592 Entry e = tab[i]; 593 if (e != null && e.get() == null) { 594 n = len; 595 removed = true; 596 i = expungeStaleEntry(i); 597 } 598 } while ( (n >>>= 1) != 0); 599 return removed; 600 } 601 602 /** 603 * Re-pack and/or re-size the table. First scan the entire 604 * table removing stale entries. If this doesn't sufficiently 605 * shrink the size of the table, double the table size. 606 */ 607 private void rehash() { 608 expungeStaleEntries(); 609 610 // Use lower threshold for doubling to avoid hysteresis 611 if (size >= threshold - threshold / 4) 612 resize(); 613 } 614 615 /** 616 * Double the capacity of the table. 617 */ 618 private void resize() { 619 Entry[] oldTab = table; 620 int oldLen = oldTab.length; 621 int newLen = oldLen * 2; 622 Entry[] newTab = new Entry[newLen]; 623 int count = 0; 624 625 for (int j = 0; j < oldLen; ++j) { 626 Entry e = oldTab[j]; 627 if (e != null) { 628 ThreadLocal k = e.get(); 629 if (k == null) { 630 e.value = null; // Help the GC 631 } else { 632 int h = k.threadLocalHashCode & (newLen - 1); 633 while (newTab[h] != null) 634 h = nextIndex(h, newLen); 635 newTab[h] = e; 636 count++; 637 } 638 } 639 } 640 641 setThreshold(newLen); 642 size = count; 643 table = newTab; 644 } 645 646 /** 647 * Expunge all stale entries in the table. 648 */ 649 private void expungeStaleEntries() { 650 Entry[] tab = table; 651 int len = tab.length; 652 for (int j = 0; j < len; j++) { 653 Entry e = tab[j]; 654 if (e != null && e.get() == null) 655 expungeStaleEntry(j); 656 } 657 } 658 } 659 }
ThreadLocal對Thread的引用所有經過局部變量完成,而沒有一個全局變量。而實際的資源副本則存儲在Thread的自身的屬性ThreadLocalMap中,這說明,其實ThreadLocal只是關聯一個Thread和其資源副本的橋樑,而且實際上Thread和資源副本的生命週期是緊密相連的,的的確確如ThreadLocal所說,在線程被回收的時候,其資源副本也會被回收,雖然ThreadLocal是靜態的,可是它既不引用Thread,也不引用ThreadLocalMap。真是精巧的設計!
5、ThreadLocal做用
其實ThreadLocal所實現的功能前面已經描述的很清楚了,可是從網上的討論來看,你們對ThreadLocal所要達成的目的意見卻不是很統一,好比博客《完全理解ThreadLocal》,具體能夠看一下類的註釋:
/** * This class provides thread-local variables. These variables differ from * their normal counterparts in that each thread that accesses one (via its * <tt>get</tt> or <tt>set</tt> method) has its own, independently initialized * copy of the variable. <tt>ThreadLocal</tt> instances are typically private * static fields in classes that wish to associate state with a thread (e.g., * a user ID or Transaction ID). */
從這個註解來看,和TLS(能夠參見《Thread Local Storage》)大體是同樣的,其實不用去深究其具體目的。我的以爲,ThreadLocal的功能就是爲每個線程提供一個資源副本,當你有這樣的需求的時候,就可使用ThreadLocal去解決問題。