标签:
Java中的ThreadLocal 可以看做以线程标识为key的Map,在多线程开发中使用非常方便。
使用示例
1 class ThreadEnv { 2 3 // 用匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值 4 private static ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() { 5 @Override 6 protected Integer initialValue() { 7 return 10; 8 } 9 }; 10 11 public int get() { 12 // 第一次get到的是初始值 13 int a = threadId.get(); 14 a++; 15 threadId.set(a); 16 return a; 17 } 18 } 19 20 public class ThreadLocalTest { 21 22 public static void main(String[] args) { 23 ThreadEnv sn = new ThreadEnv(); 24 new TestClient(sn); 25 new TestClient(sn); 26 new TestClient(sn); 27 } 28 29 private static class TestClient extends Thread { 30 private ThreadEnv sn; 31 32 public TestClient(ThreadEnv sn) { 33 this.sn = sn; 34 this.start(); 35 } 36 37 public void run() { 38 for (int i = 0; i < 3; i++) { 39 System.out.println(Thread.currentThread()+ " >>> " + sn.get()); 40 } 41 } 42 } 43 44 }
运行结果
Thread[Thread-2,5,main] >>> 11 Thread[Thread-1,5,main] >>> 11 Thread[Thread-0,5,main] >>> 11 Thread[Thread-1,5,main] >>> 12 Thread[Thread-1,5,main] >>> 13 Thread[Thread-2,5,main] >>> 12 Thread[Thread-0,5,main] >>> 12 Thread[Thread-0,5,main] >>> 13 Thread[Thread-2,5,main] >>> 13
源码解析
1 public ThreadLocal() { 2 } 3 4 /** 5 * Returns the value in the current thread‘s copy of this 6 * thread-local variable. If the variable has no value for the 7 * current thread, it is first initialized to the value returned 8 * by an invocation of the {@link #initialValue} method. 9 * 10 * @return the current thread‘s value of this thread-local 11 */ 12 public T get() { 13 Thread t = Thread.currentThread(); 14 ThreadLocalMap map = getMap(t); 15 if (map != null) { 16 ThreadLocalMap.Entry e = map.getEntry(this); 17 if (e != null) { 18 @SuppressWarnings("unchecked") 19 T result = (T)e.value; 20 return result; 21 } 22 } 23 return setInitialValue(); 24 } 25 26 /** 27 * Variant of set() to establish initialValue. Used instead 28 * of set() in case user has overridden the set() method. 29 * 30 * @return the initial value 31 */ 32 private T setInitialValue() { 33 T value = initialValue(); 34 Thread t = Thread.currentThread(); 35 ThreadLocalMap map = getMap(t); 36 if (map != null) 37 map.set(this, value); 38 else 39 createMap(t, value); 40 return value; 41 } 42 43 /** 44 * Sets the current thread‘s copy of this thread-local variable 45 * to the specified value. Most subclasses will have no need to 46 * override this method, relying solely on the {@link #initialValue} 47 * method to set the values of thread-locals. 48 * 49 * @param value the value to be stored in the current thread‘s copy of 50 * this thread-local. 51 */ 52 public void set(T value) { 53 Thread t = Thread.currentThread(); 54 ThreadLocalMap map = getMap(t); 55 if (map != null) 56 map.set(this, value); 57 else 58 createMap(t, value); 59 } 60 61 /** 62 * Removes the current thread‘s value for this thread-local 63 * variable. If this thread-local variable is subsequently 64 * {@linkplain #get read} by the current thread, its value will be 65 * reinitialized by invoking its {@link #initialValue} method, 66 * unless its value is {@linkplain #set set} by the current thread 67 * in the interim. This may result in multiple invocations of the 68 * {@code initialValue} method in the current thread. 69 * 70 * @since 1.5 71 */ 72 public void remove() { 73 ThreadLocalMap m = getMap(Thread.currentThread()); 74 if (m != null) 75 m.remove(this); 76 } 77 78 /** 79 * Get the map associated with a ThreadLocal. Overridden in 80 * InheritableThreadLocal. 81 * 82 * @param t the current thread 83 * @return the map 84 */ 85 ThreadLocalMap getMap(Thread t) { 86 return t.threadLocals; 87 } 88 89 /** 90 * Create the map associated with a ThreadLocal. Overridden in 91 * InheritableThreadLocal. 92 * 93 * @param t the current thread 94 * @param firstValue value for the initial entry of the map 95 */ 96 void createMap(Thread t, T firstValue) { 97 t.threadLocals = new ThreadLocalMap(this, firstValue); 98 } 99 100 /** 101 * Factory method to create map of inherited thread locals. 102 * Designed to be called only from Thread constructor. 103 * 104 * @param parentMap the map associated with parent thread 105 * @return a map containing the parent‘s inheritable bindings 106 */ 107 static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) { 108 return new ThreadLocalMap(parentMap); 109 } 110 111 /** 112 * Method childValue is visibly defined in subclass 113 * InheritableThreadLocal, but is internally defined here for the 114 * sake of providing createInheritedMap factory method without 115 * needing to subclass the map class in InheritableThreadLocal. 116 * This technique is preferable to the alternative of embedding 117 * instanceof tests in methods. 118 */ 119 T childValue(T parentValue) { 120 throw new UnsupportedOperationException(); 121 } 122 123 /** 124 * An extension of ThreadLocal that obtains its initial value from 125 * the specified {@code Supplier}. 126 */ 127 static final class SuppliedThreadLocal<T> extends ThreadLocal<T> { 128 129 private final Supplier<? extends T> supplier; 130 131 SuppliedThreadLocal(Supplier<? extends T> supplier) { 132 this.supplier = Objects.requireNonNull(supplier); 133 } 134 135 @Override 136 protected T initialValue() { 137 return supplier.get(); 138 } 139 } 140 141 /** 142 * ThreadLocalMap is a customized hash map suitable only for 143 * maintaining thread local values. No operations are exported 144 * outside of the ThreadLocal class. The class is package private to 145 * allow declaration of fields in class Thread. To help deal with 146 * very large and long-lived usages, the hash table entries use 147 * WeakReferences for keys. However, since reference queues are not 148 * used, stale entries are guaranteed to be removed only when 149 * the table starts running out of space. 150 */ 151 static class ThreadLocalMap { 152 153 /** 154 * The entries in this hash map extend WeakReference, using 155 * its main ref field as the key (which is always a 156 * ThreadLocal object). Note that null keys (i.e. entry.get() 157 * == null) mean that the key is no longer referenced, so the 158 * entry can be expunged from table. Such entries are referred to 159 * as "stale entries" in the code that follows. 160 */ 161 static class Entry extends WeakReference<ThreadLocal<?>> { 162 /** The value associated with this ThreadLocal. */ 163 Object value; 164 165 Entry(ThreadLocal<?> k, Object v) { 166 super(k); 167 value = v; 168 } 169 } 170 171 /** 172 * The initial capacity -- MUST be a power of two. 173 */ 174 private static final int INITIAL_CAPACITY = 16; 175 176 /** 177 * The table, resized as necessary. 178 * table.length MUST always be a power of two. 179 */ 180 private Entry[] table; 181 182 /** 183 * The number of entries in the table. 184 */ 185 private int size = 0; 186 187 /** 188 * The next size value at which to resize. 189 */ 190 private int threshold; // Default to 0 191 192 /** 193 * Set the resize threshold to maintain at worst a 2/3 load factor. 194 */ 195 private void setThreshold(int len) { 196 threshold = len * 2 / 3; 197 } 198 199 /** 200 * Increment i modulo len. 201 */ 202 private static int nextIndex(int i, int len) { 203 return ((i + 1 < len) ? i + 1 : 0); 204 } 205 206 /** 207 * Decrement i modulo len. 208 */ 209 private static int prevIndex(int i, int len) { 210 return ((i - 1 >= 0) ? i - 1 : len - 1); 211 } 212 213 /** 214 * Construct a new map initially containing (firstKey, firstValue). 215 * ThreadLocalMaps are constructed lazily, so we only create 216 * one when we have at least one entry to put in it. 217 */ 218 ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { 219 table = new Entry[INITIAL_CAPACITY]; 220 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); 221 table[i] = new Entry(firstKey, firstValue); 222 size = 1; 223 setThreshold(INITIAL_CAPACITY); 224 } 225 226 /** 227 * Construct a new map including all Inheritable ThreadLocals 228 * from given parent map. Called only by createInheritedMap. 229 * 230 * @param parentMap the map associated with parent thread. 231 */ 232 private ThreadLocalMap(ThreadLocalMap parentMap) { 233 Entry[] parentTable = parentMap.table; 234 int len = parentTable.length; 235 setThreshold(len); 236 table = new Entry[len]; 237 238 for (int j = 0; j < len; j++) { 239 Entry e = parentTable[j]; 240 if (e != null) { 241 @SuppressWarnings("unchecked") 242 ThreadLocal<Object> key = (ThreadLocal<Object>) e.get(); 243 if (key != null) { 244 Object value = key.childValue(e.value); 245 Entry c = new Entry(key, value); 246 int h = key.threadLocalHashCode & (len - 1); 247 while (table[h] != null) 248 h = nextIndex(h, len); 249 table[h] = c; 250 size++; 251 } 252 } 253 } 254 } 255 256 /** 257 * Get the entry associated with key. This method 258 * itself handles only the fast path: a direct hit of existing 259 * key. It otherwise relays to getEntryAfterMiss. This is 260 * designed to maximize performance for direct hits, in part 261 * by making this method readily inlinable. 262 * 263 * @param key the thread local object 264 * @return the entry associated with key, or null if no such 265 */ 266 private Entry getEntry(ThreadLocal<?> key) { 267 int i = key.threadLocalHashCode & (table.length - 1); 268 Entry e = table[i]; 269 if (e != null && e.get() == key) 270 return e; 271 else 272 return getEntryAfterMiss(key, i, e); 273 } 274 275 /** 276 * Version of getEntry method for use when key is not found in 277 * its direct hash slot. 278 * 279 * @param key the thread local object 280 * @param i the table index for key‘s hash code 281 * @param e the entry at table[i] 282 * @return the entry associated with key, or null if no such 283 */ 284 private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) { 285 Entry[] tab = table; 286 int len = tab.length; 287 288 while (e != null) { 289 ThreadLocal<?> k = e.get(); 290 if (k == key) 291 return e; 292 if (k == null) 293 expungeStaleEntry(i); 294 else 295 i = nextIndex(i, len); 296 e = tab[i]; 297 } 298 return null; 299 } 300 301 /** 302 * Set the value associated with key. 303 * 304 * @param key the thread local object 305 * @param value the value to be set 306 */ 307 private void set(ThreadLocal<?> key, Object value) { 308 309 // We don‘t use a fast path as with get() because it is at 310 // least as common to use set() to create new entries as 311 // it is to replace existing ones, in which case, a fast 312 // path would fail more often than not. 313 314 Entry[] tab = table; 315 int len = tab.length; 316 int i = key.threadLocalHashCode & (len-1); 317 318 for (Entry e = tab[i]; 319 e != null; 320 e = tab[i = nextIndex(i, len)]) { 321 ThreadLocal<?> k = e.get(); 322 323 if (k == key) { 324 e.value = value; 325 return; 326 } 327 328 if (k == null) { 329 replaceStaleEntry(key, value, i); 330 return; 331 } 332 } 333 334 tab[i] = new Entry(key, value); 335 int sz = ++size; 336 if (!cleanSomeSlots(i, sz) && sz >= threshold) 337 rehash(); 338 } 339 340 /** 341 * Remove the entry for key. 342 */ 343 private void remove(ThreadLocal<?> key) { 344 Entry[] tab = table; 345 int len = tab.length; 346 int i = key.threadLocalHashCode & (len-1); 347 for (Entry e = tab[i]; 348 e != null; 349 e = tab[i = nextIndex(i, len)]) { 350 if (e.get() == key) { 351 e.clear(); 352 expungeStaleEntry(i); 353 return; 354 } 355 } 356 } 357 358 /** 359 * Replace a stale entry encountered during a set operation 360 * with an entry for the specified key. The value passed in 361 * the value parameter is stored in the entry, whether or not 362 * an entry already exists for the specified key. 363 * 364 * As a side effect, this method expunges all stale entries in the 365 * "run" containing the stale entry. (A run is a sequence of entries 366 * between two null slots.) 367 * 368 * @param key the key 369 * @param value the value to be associated with key 370 * @param staleSlot index of the first stale entry encountered while 371 * searching for key. 372 */ 373 private void replaceStaleEntry(ThreadLocal<?> key, Object value, 374 int staleSlot) { 375 Entry[] tab = table; 376 int len = tab.length; 377 Entry e; 378 379 // Back up to check for prior stale entry in current run. 380 // We clean out whole runs at a time to avoid continual 381 // incremental rehashing due to garbage collector freeing 382 // up refs in bunches (i.e., whenever the collector runs). 383 int slotToExpunge = staleSlot; 384 for (int i = prevIndex(staleSlot, len); 385 (e = tab[i]) != null; 386 i = prevIndex(i, len)) 387 if (e.get() == null) 388 slotToExpunge = i; 389 390 // Find either the key or trailing null slot of run, whichever 391 // occurs first 392 for (int i = nextIndex(staleSlot, len); 393 (e = tab[i]) != null; 394 i = nextIndex(i, len)) { 395 ThreadLocal<?> k = e.get(); 396 397 // If we find key, then we need to swap it 398 // with the stale entry to maintain hash table order. 399 // The newly stale slot, or any other stale slot 400 // encountered above it, can then be sent to expungeStaleEntry 401 // to remove or rehash all of the other entries in run. 402 if (k == key) { 403 e.value = value; 404 405 tab[i] = tab[staleSlot]; 406 tab[staleSlot] = e; 407 408 // Start expunge at preceding stale entry if it exists 409 if (slotToExpunge == staleSlot) 410 slotToExpunge = i; 411 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); 412 return; 413 } 414 415 // If we didn‘t find stale entry on backward scan, the 416 // first stale entry seen while scanning for key is the 417 // first still present in the run. 418 if (k == null && slotToExpunge == staleSlot) 419 slotToExpunge = i; 420 } 421 422 // If key not found, put new entry in stale slot 423 tab[staleSlot].value = null; 424 tab[staleSlot] = new Entry(key, value); 425 426 // If there are any other stale entries in run, expunge them 427 if (slotToExpunge != staleSlot) 428 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); 429 } 430 431 /** 432 * Expunge a stale entry by rehashing any possibly colliding entries 433 * lying between staleSlot and the next null slot. This also expunges 434 * any other stale entries encountered before the trailing null. See 435 * Knuth, Section 6.4 436 * 437 * @param staleSlot index of slot known to have null key 438 * @return the index of the next null slot after staleSlot 439 * (all between staleSlot and this slot will have been checked 440 * for expunging). 441 */ 442 private int expungeStaleEntry(int staleSlot) { 443 Entry[] tab = table; 444 int len = tab.length; 445 446 // expunge entry at staleSlot 447 tab[staleSlot].value = null; 448 tab[staleSlot] = null; 449 size--; 450 451 // Rehash until we encounter null 452 Entry e; 453 int i; 454 for (i = nextIndex(staleSlot, len); 455 (e = tab[i]) != null; 456 i = nextIndex(i, len)) { 457 ThreadLocal<?> k = e.get(); 458 if (k == null) { 459 e.value = null; 460 tab[i] = null; 461 size--; 462 } else { 463 int h = k.threadLocalHashCode & (len - 1); 464 if (h != i) { 465 tab[i] = null; 466 467 // Unlike Knuth 6.4 Algorithm R, we must scan until 468 // null because multiple entries could have been stale. 469 while (tab[h] != null) 470 h = nextIndex(h, len); 471 tab[h] = e; 472 } 473 } 474 } 475 return i; 476 } 477 478 /** 479 * Heuristically scan some cells looking for stale entries. 480 * This is invoked when either a new element is added, or 481 * another stale one has been expunged. It performs a 482 * logarithmic number of scans, as a balance between no 483 * scanning (fast but retains garbage) and a number of scans 484 * proportional to number of elements, that would find all 485 * garbage but would cause some insertions to take O(n) time. 486 * 487 * @param i a position known NOT to hold a stale entry. The 488 * scan starts at the element after i. 489 * 490 * @param n scan control: {@code log2(n)} cells are scanned, 491 * unless a stale entry is found, in which case 492 * {@code log2(table.length)-1} additional cells are scanned. 493 * When called from insertions, this parameter is the number 494 * of elements, but when from replaceStaleEntry, it is the 495 * table length. (Note: all this could be changed to be either 496 * more or less aggressive by weighting n instead of just 497 * using straight log n. But this version is simple, fast, and 498 * seems to work well.) 499 * 500 * @return true if any stale entries have been removed. 501 */ 502 private boolean cleanSomeSlots(int i, int n) { 503 boolean removed = false; 504 Entry[] tab = table; 505 int len = tab.length; 506 do { 507 i = nextIndex(i, len); 508 Entry e = tab[i]; 509 if (e != null && e.get() == null) { 510 n = len; 511 removed = true; 512 i = expungeStaleEntry(i); 513 } 514 } while ( (n >>>= 1) != 0); 515 return removed; 516 } 517 518 /** 519 * Re-pack and/or re-size the table. First scan the entire 520 * table removing stale entries. If this doesn‘t sufficiently 521 * shrink the size of the table, double the table size. 522 */ 523 private void rehash() { 524 expungeStaleEntries(); 525 526 // Use lower threshold for doubling to avoid hysteresis 527 if (size >= threshold - threshold / 4) 528 resize(); 529 } 530 531 /** 532 * Double the capacity of the table. 533 */ 534 private void resize() { 535 Entry[] oldTab = table; 536 int oldLen = oldTab.length; 537 int newLen = oldLen * 2; 538 Entry[] newTab = new Entry[newLen]; 539 int count = 0; 540 541 for (int j = 0; j < oldLen; ++j) { 542 Entry e = oldTab[j]; 543 if (e != null) { 544 ThreadLocal<?> k = e.get(); 545 if (k == null) { 546 e.value = null; // Help the GC 547 } else { 548 int h = k.threadLocalHashCode & (newLen - 1); 549 while (newTab[h] != null) 550 h = nextIndex(h, newLen); 551 newTab[h] = e; 552 count++; 553 } 554 } 555 } 556 557 setThreshold(newLen); 558 size = count; 559 table = newTab; 560 } 561 562 /** 563 * Expunge all stale entries in the table. 564 */ 565 private void expungeStaleEntries() { 566 Entry[] tab = table; 567 int len = tab.length; 568 for (int j = 0; j < len; j++) { 569 Entry e = tab[j]; 570 if (e != null && e.get() == null) 571 expungeStaleEntry(j); 572 } 573 } 574 } 575 }
标签:
原文地址:http://www.cnblogs.com/luangeng/p/5785527.html