2013-12-23 17:44:44
Java为线程安全提供了一些工具类,如ThreadLocal类,它代表一个线程局部变量,通过把数据放在ThreadLocal中就可以让每个线程创建一个该变量的副本,从而避免并发访问的线程安全问题。
线程局部变量的功能其实很简单,就是为每一个使用该变量的线程提供一个副本,使每一个线程都可以独立的访问属于自己的副本,而不会和其他线程的副本产生冲突,就好像每一个线程都完全拥有该变量一样。
ThreadLocal类并不能替代同步机制,两者面向的问题领域不同。
同步机制是为了同步多个线程对相同资源的并发访问,是多个线程之间进行通信的有效方式;
ThreadLocal是为了隔离多个线程的数据共享,从根本上避免多个线程对共享资源的竞争,也就不需要对多个线程进行同步了。
自己做了一个Demo, 如下:
ThreadLocalTest.java
1 package thread; 2 3 public class ThreadLocalTest { 4 5 public ThreadLocal<String> local = new ThreadLocal<String>(); 6 7 private String name; 8 9 public ThreadLocalTest() { 10 } 11 12 public String getName() { 13 // return name; 14 return local.get(); 15 } 16 17 public void setName(String name) { 18 // this.name = name; 19 local.set(name); 20 } 21 22 public static void main(String[] args) { 23 ThreadLocalTest one = new ThreadLocalTest(); 24 ThreadOne t1 = new ThreadOne(one); 25 ThreadTwo t2 = new ThreadTwo(one); 26 t1.start(); 27 t2.start(); 28 } 29 }
ThreadOne.java
1 package thread; 2 3 public class ThreadOne extends Thread { 4 ThreadLocalTest one; 5 6 public ThreadOne(ThreadLocalTest o) { 7 one = o; 8 } 9 10 @Override 11 public void run() { 12 for (int i = 0; i < 10; i++) { 13 StringBuilder ss = new StringBuilder(" one_"); 14 ss.append(i); 15 one.setName(ss.toString()); 16 try { 17 Thread.sleep(280); 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21 System.out.println("线程一:" + Thread.currentThread().getName() 22 + one.getName()); 23 } 24 } 25 }
ThreadTwo.java
1 package thread; 2 3 public class ThreadTwo extends Thread { 4 ThreadLocalTest one; 5 6 public ThreadTwo(ThreadLocalTest o) { 7 one = o; 8 } 9 10 @Override 11 public void run() { 12 for (int i = 0; i < 10; i++) { 13 StringBuilder ss = new StringBuilder("------------two_"); 14 ss.append(i); 15 one.setName(ss.toString()); 16 try { 17 Thread.sleep(100); 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21 System.out.println("线程二:" + Thread.currentThread().getName() 22 + one.getName()); 23 } 24 } 25 }
先看不是用ThreadLocal的log(使用ThreadLocalTest.java中注释掉的部分),如下:
1 线程二:Thread-1------------two_0 2 线程二:Thread-1------------two_1 3 线程一:Thread-0------------two_2 4 线程二:Thread-1 one_1 5 线程二:Thread-1------------two_3 6 线程二:Thread-1------------two_4 7 线程一:Thread-0------------two_5 8 线程二:Thread-1 one_2 9 线程二:Thread-1------------two_6 10 线程二:Thread-1------------two_7 11 线程一:Thread-0------------two_8 12 线程二:Thread-1 one_3 13 线程二:Thread-1------------two_9 14 线程一:Thread-0------------two_9 15 线程一:Thread-0 one_4 16 线程一:Thread-0 one_5 17 线程一:Thread-0 one_6 18 线程一:Thread-0 one_7 19 线程一:Thread-0 one_8 20 线程一:Thread-0 one_9
可以上面红色部分,可以发现就是多线程导致资源混乱的情况。
下面是使用了ThreadLocal后的log:
1 线程二:Thread-1------------two_0 2 线程二:Thread-1------------two_1 3 线程一:Thread-0 one_0 4 线程二:Thread-1------------two_2 5 线程二:Thread-1------------two_3 6 线程二:Thread-1------------two_4 7 线程一:Thread-0 one_1 8 线程二:Thread-1------------two_5 9 线程二:Thread-1------------two_6 10 线程二:Thread-1------------two_7 11 线程一:Thread-0 one_2 12 线程二:Thread-1------------two_8 13 线程二:Thread-1------------two_9 14 线程一:Thread-0 one_3 15 线程一:Thread-0 one_4 16 线程一:Thread-0 one_5 17 线程一:Thread-0 one_6 18 线程一:Thread-0 one_7 19 线程一:Thread-0 one_8 20 线程一:Thread-0 one_9
一切正常。
但是我发先了一个问题,那就是ThreadLocal类在Android和Java中的实现不太一样,把它们的源码都贴出来,有兴趣的同学可以好好研究一下:
Java SE-1.6
1 /* 2 * %W% %E% 3 * 4 * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved. 5 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 6 */ 7 8 package java.lang; 9 import java.lang.ref.*; 10 import java.util.concurrent.atomic.AtomicInteger; 11 12 /** 13 * This class provides thread-local variables. These variables differ from 14 * their normal counterparts in that each thread that accesses one (via its 15 * <tt>get</tt> or <tt>set</tt> method) has its own, independently initialized 16 * copy of the variable. <tt>ThreadLocal</tt> instances are typically private 17 * static fields in classes that wish to associate state with a thread (e.g., 18 * a user ID or Transaction ID). 19 * 20 * <p>For example, the class below generates unique identifiers local to each 21 * thread. 22 * A thread's id is 23 * assigned the first time it invokes <tt>UniqueThreadIdGenerator.getCurrentThreadId()</tt> and remains unchanged on subsequent calls. 24 * <pre> 25 * import java.util.concurrent.atomic.AtomicInteger; 26 * 27 * public class UniqueThreadIdGenerator { 28 * 29 * private static final AtomicInteger uniqueId = new AtomicInteger(0); 30 * 31 * private static final ThreadLocal < Integer > uniqueNum = 32 * new ThreadLocal < Integer > () { 33 * @Override protected Integer initialValue() { 34 * return uniqueId.getAndIncrement(); 35 * } 36 * }; 37 * 38 * public static int getCurrentThreadId() { 39 * return uniqueId.get(); 40 * } 41 * } // UniqueThreadIdGenerator 42 * </pre> 43 * <p>Each thread holds an implicit reference to its copy of a thread-local 44 * variable as long as the thread is alive and the <tt>ThreadLocal</tt> 45 * instance is accessible; after a thread goes away, all of its copies of 46 * thread-local instances are subject to garbage collection (unless other 47 * references to these copies exist). 48 * 49 * @author Josh Bloch and Doug Lea 50 * @version %I%, %G% 51 * @since 1.2 52 */ 53 public class ThreadLocal<T> { 54 /** 55 * ThreadLocals rely on per-thread linear-probe hash maps attached 56 * to each thread (Thread.threadLocals and 57 * inheritableThreadLocals). The ThreadLocal objects act as keys, 58 * searched via threadLocalHashCode. This is a custom hash code 59 * (useful only within ThreadLocalMaps) that eliminates collisions 60 * in the common case where consecutively constructed ThreadLocals 61 * are used by the same threads, while remaining well-behaved in 62 * less common cases. 63 */ 64 private final int threadLocalHashCode = nextHashCode(); 65 66 /** 67 * The next hash code to be given out. Updated atomically. Starts at 68 * zero. 69 */ 70 private static AtomicInteger nextHashCode = 71 new AtomicInteger(); 72 73 /** 74 * The difference between successively generated hash codes - turns 75 * implicit sequential thread-local IDs into near-optimally spread 76 * multiplicative hash values for power-of-two-sized tables. 77 */ 78 private static final int HASH_INCREMENT = 0x61c88647; 79 80 /** 81 * Returns the next hash code. 82 */ 83 private static int nextHashCode() { 84 return nextHashCode.getAndAdd(HASH_INCREMENT); 85 } 86 87 /** 88 * Returns the current thread's "initial value" for this 89 * thread-local variable. This method will be invoked the first 90 * time a thread accesses the variable with the {@link #get} 91 * method, unless the thread previously invoked the {@link #set} 92 * method, in which case the <tt>initialValue</tt> method will not 93 * be invoked for the thread. Normally, this method is invoked at 94 * most once per thread, but it may be invoked again in case of 95 * subsequent invocations of {@link #remove} followed by {@link #get}. 96 * 97 * <p>This implementation simply returns <tt>null</tt>; if the 98 * programmer desires thread-local variables to have an initial 99 * value other than <tt>null</tt>, <tt>ThreadLocal</tt> must be 100 * subclassed, and this method overridden. Typically, an 101 * anonymous inner class will be used. 102 * 103 * @return the initial value for this thread-local 104 */ 105 protected T initialValue() { 106 return null; 107 } 108 109 /** 110 * Creates a thread local variable. 111 */ 112 public ThreadLocal() { 113 } 114 115 /** 116 * Returns the value in the current thread's copy of this 117 * thread-local variable. If the variable has no value for the 118 * current thread, it is first initialized to the value returned 119 * by an invocation of the {@link #initialValue} method. 120 * 121 * @return the current thread's value of this thread-local 122 */ 123 public T get() { 124 Thread t = Thread.currentThread(); 125 ThreadLocalMap map = getMap(t); 126 if (map != null) { 127 ThreadLocalMap.Entry e = map.getEntry(this); 128 if (e != null) 129 return (T)e.value; 130 } 131 return setInitialValue(); 132 } 133 134 /** 135 * Variant of set() to establish initialValue. Used instead 136 * of set() in case user has overridden the set() method. 137 * 138 * @return the initial value 139 */ 140 private T setInitialValue() { 141 T value = initialValue(); 142 Thread t = Thread.currentThread(); 143 ThreadLocalMap map = getMap(t); 144 if (map != null) 145 map.set(this, value); 146 else 147 createMap(t, value); 148 return value; 149 } 150 151 /** 152 * Sets the current thread's copy of this thread-local variable 153 * to the specified value. Most subclasses will have no need to 154 * override this method, relying solely on the {@link #initialValue} 155 * method to set the values of thread-locals. 156 * 157 * @param value the value to be stored in the current thread's copy of 158 * this thread-local. 159 */ 160 public void set(T value) { 161 Thread t = Thread.currentThread(); 162 ThreadLocalMap map = getMap(t); 163 if (map != null) 164 map.set(this, value); 165 else 166 createMap(t, value); 167 } 168 169 /** 170 * Removes the current thread's value for this thread-local 171 * variable. If this thread-local variable is subsequently 172 * {@linkplain #get read} by the current thread, its value will be 173 * reinitialized by invoking its {@link #initialValue} method, 174 * unless its value is {@linkplain #set set} by the current thread 175 * in the interim. This may result in multiple invocations of the 176 * <tt>initialValue</tt> method in the current thread. 177 * 178 * @since 1.5 179 */ 180 public void remove() { 181 ThreadLocalMap m = getMap(Thread.currentThread()); 182 if (m != null) 183 m.remove(this); 184 } 185 186 /** 187 * Get the map associated with a ThreadLocal. Overridden in 188 * InheritableThreadLocal. 189 * 190 * @param t the current thread 191 * @return the map 192 */ 193 ThreadLocalMap getMap(Thread t) { 194 return t.threadLocals; 195 } 196 197 /** 198 * Create the map associated with a ThreadLocal. Overridden in 199 * InheritableThreadLocal. 200 * 201 * @param t the current thread 202 * @param firstValue value for the initial entry of the map 203 * @param map the map to store. 204 */ 205 void createMap(Thread t, T firstValue) { 206 t.threadLocals = new ThreadLocalMap(this, firstValue); 207 } 208 209 /** 210 * Factory method to create map of inherited thread locals. 211 * Designed to be called only from Thread constructor. 212 * 213 * @param parentMap the map associated with parent thread 214 * @return a map containing the parent's inheritable bindings 215 */ 216 static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) { 217 return new ThreadLocalMap(parentMap); 218 } 219 220 /** 221 * Method childValue is visibly defined in subclass 222 * InheritableThreadLocal, but is internally defined here for the 223 * sake of providing createInheritedMap factory method without 224 * needing to subclass the map class in InheritableThreadLocal. 225 * This technique is preferable to the alternative of embedding 226 * instanceof tests in methods. 227 */ 228 T childValue(T parentValue) { 229 throw new UnsupportedOperationException(); 230 } 231 232 /** 233 * ThreadLocalMap is a customized hash map suitable only for 234 * maintaining thread local values. No operations are exported 235 * outside of the ThreadLocal class. The class is package private to 236 * allow declaration of fields in class Thread. To help deal with 237 * very large and long-lived usages, the hash table entries use 238 * WeakReferences for keys. However, since reference queues are not 239 * used, stale entries are guaranteed to be removed only when 240 * the table starts running out of space. 241 */ 242 static class ThreadLocalMap { 243 244 /** 245 * The entries in this hash map extend WeakReference, using 246 * its main ref field as the key (which is always a 247 * ThreadLocal object). Note that null keys (i.e. entry.get() 248 * == null) mean that the key is no longer referenced, so the 249 * entry can be expunged from table. Such entries are referred to 250 * as "stale entries" in the code that follows. 251 */ 252 static class Entry extends WeakReference<ThreadLocal> { 253 /** The value associated with this ThreadLocal. */ 254 Object value; 255 256 Entry(ThreadLocal k, Object v) { 257 super(k); 258 value = v; 259 } 260 } 261 262 /** 263 * The initial capacity -- MUST be a power of two. 264 */ 265 private static final int INITIAL_CAPACITY = 16; 266 267 /** 268 * The table, resized as necessary. 269 * table.length MUST always be a power of two. 270 */ 271 private Entry[] table; 272 273 /** 274 * The number of entries in the table. 275 */ 276 private int size = 0; 277 278 /** 279 * The next size value at which to resize. 280 */ 281 private int threshold; // Default to 0 282 283 /** 284 * Set the resize threshold to maintain at worst a 2/3 load factor. 285 */ 286 private void setThreshold(int len) { 287 threshold = len * 2 / 3; 288 } 289 290 /** 291 * Increment i modulo len. 292 */ 293 private static int nextIndex(int i, int len) { 294 return ((i + 1 < len) ? i + 1 : 0); 295 } 296 297 /** 298 * Decrement i modulo len. 299 */ 300 private static int prevIndex(int i, int len) { 301 return ((i - 1 >= 0) ? i - 1 : len - 1); 302 } 303 304 /** 305 * Construct a new map initially containing (firstKey, firstValue). 306 * ThreadLocalMaps are constructed lazily, so we only create 307 * one when we have at least one entry to put in it. 308 */ 309 ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { 310 table = new Entry[INITIAL_CAPACITY]; 311 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); 312 table[i] = new Entry(firstKey, firstValue); 313 size = 1; 314 setThreshold(INITIAL_CAPACITY); 315 } 316 317 /** 318 * Construct a new map including all Inheritable ThreadLocals 319 * from given parent map. Called only by createInheritedMap. 320 * 321 * @param parentMap the map associated with parent thread. 322 */ 323 private ThreadLocalMap(ThreadLocalMap parentMap) { 324 Entry[] parentTable = parentMap.table; 325 int len = parentTable.length; 326 setThreshold(len); 327 table = new Entry[len]; 328 329 for (int j = 0; j < len; j++) { 330 Entry e = parentTable[j]; 331 if (e != null) { 332 ThreadLocal key = e.get(); 333 if (key != null) { 334 Object value = key.childValue(e.value); 335 Entry c = new Entry(key, value); 336 int h = key.threadLocalHashCode & (len - 1); 337 while (table[h] != null) 338 h = nextIndex(h, len); 339 table[h] = c; 340 size++; 341 } 342 } 343 } 344 } 345 346 /** 347 * Get the entry associated with key. This method 348 * itself handles only the fast path: a direct hit of existing 349 * key. It otherwise relays to getEntryAfterMiss. This is 350 * designed to maximize performance for direct hits, in part 351 * by making this method readily inlinable. 352 * 353 * @param key the thread local object 354 * @return the entry associated with key, or null if no such 355 */ 356 private Entry getEntry(ThreadLocal key) { 357 int i = key.threadLocalHashCode & (table.length - 1); 358 Entry e = table[i]; 359 if (e != null && e.get() == key) 360 return e; 361 else 362 return getEntryAfterMiss(key, i, e); 363 } 364 365 /** 366 * Version of getEntry method for use when key is not found in 367 * its direct hash slot. 368 * 369 * @param key the thread local object 370 * @param i the table index for key's hash code 371 * @param e the entry at table[i] 372 * @return the entry associated with key, or null if no such 373 */ 374 private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) { 375 Entry[] tab = table; 376 int len = tab.length; 377 378 while (e != null) { 379 ThreadLocal k = e.get(); 380 if (k == key) 381 return e; 382 if (k == null) 383 expungeStaleEntry(i); 384 else 385 i = nextIndex(i, len); 386 e = tab[i]; 387 } 388 return null; 389 } 390 391 /** 392 * Set the value associated with key. 393 * 394 * @param key the thread local object 395 * @param value the value to be set 396 */ 397 private void set(ThreadLocal key, Object value) { 398 399 // We don't use a fast path as with get() because it is at 400 // least as common to use set() to create new entries as 401 // it is to replace existing ones, in which case, a fast 402 // path would fail more often than not. 403 404 Entry[] tab = table; 405 int len = tab.length; 406 int i = key.threadLocalHashCode & (len-1); 407 408 for (Entry e = tab[i]; 409 e != null; 410 e = tab[i = nextIndex(i, len)]) { 411 ThreadLocal k = e.get(); 412 413 if (k == key) { 414 e.value = value; 415 return; 416 } 417 418 if (k == null) { 419 replaceStaleEntry(key, value, i); 420 return; 421 } 422 } 423 424 tab[i] = new Entry(key, value); 425 int sz = ++size; 426 if (!cleanSomeSlots(i, sz) && sz >= threshold) 427 rehash(); 428 } 429 430 /** 431 * Remove the entry for key. 432 */ 433 private void remove(ThreadLocal key) { 434 Entry[] tab = table; 435 int len = tab.length; 436 int i = key.threadLocalHashCode & (len-1); 437 for (Entry e = tab[i]; 438 e != null; 439 e = tab[i = nextIndex(i, len)]) { 440 if (e.get() == key) { 441 e.clear(); 442 expungeStaleEntry(i); 443 return; 444 } 445 } 446 } 447 448 /** 449 * Replace a stale entry encountered during a set operation 450 * with an entry for the specified key. The value passed in 451 * the value parameter is stored in the entry, whether or not 452 * an entry already exists for the specified key. 453 * 454 * As a side effect, this method expunges all stale entries in the 455 * "run" containing the stale entry. (A run is a sequence of entries 456 * between two null slots.) 457 * 458 * @param key the key 459 * @param value the value to be associated with key 460 * @param staleSlot index of the first stale entry encountered while 461 * searching for key. 462 */ 463 private void replaceStaleEntry(ThreadLocal key, Object value, 464 int staleSlot) { 465 Entry[] tab = table; 466 int len = tab.length; 467 Entry e; 468 469 // Back up to check for prior stale entry in current run. 470 // We clean out whole runs at a time to avoid continual 471 // incremental rehashing due to garbage collector freeing 472 // up refs in bunches (i.e., whenever the collector runs). 473 int slotToExpunge = staleSlot; 474 for (int i = prevIndex(staleSlot, len); 475 (e = tab[i]) != null; 476 i = prevIndex(i, len)) 477 if (e.get() == null) 478 slotToExpunge = i; 479 480 // Find either the key or trailing null slot of run, whichever 481 // occurs first 482 for (int i = nextIndex(staleSlot, len); 483 (e = tab[i]) != null; 484 i = nextIndex(i, len)) { 485 ThreadLocal k = e.get(); 486 487 // If we find key, then we need to swap it 488 // with the stale entry to maintain hash table order. 489 // The newly stale slot, or any other stale slot 490 // encountered above it, can then be sent to expungeStaleEntry 491 // to remove or rehash all of the other entries in run. 492 if (k == key) { 493 e.value = value; 494 495 tab[i] = tab[staleSlot]; 496 tab[staleSlot] = e; 497 498 // Start expunge at preceding stale entry if it exists 499 if (slotToExpunge == staleSlot) 500 slotToExpunge = i; 501 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); 502 return; 503 } 504 505 // If we didn't find stale entry on backward scan, the 506 // first stale entry seen while scanning for key is the 507 // first still present in the run. 508 if (k == null && slotToExpunge == staleSlot) 509 slotToExpunge = i; 510 } 511 512 // If key not found, put new entry in stale slot 513 tab[staleSlot].value = null; 514 tab[staleSlot] = new Entry(key, value); 515 516 // If there are any other stale entries in run, expunge them 517 if (slotToExpunge != staleSlot) 518 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); 519 } 520 521 /** 522 * Expunge a stale entry by rehashing any possibly colliding entries 523 * lying between staleSlot and the next null slot. This also expunges 524 * any other stale entries encountered before the trailing null. See 525 * Knuth, Section 6.4 526 * 527 * @param staleSlot index of slot known to have null key 528 * @return the index of the next null slot after staleSlot 529 * (all between staleSlot and this slot will have been checked 530 * for expunging). 531 */ 532 private int expungeStaleEntry(int staleSlot) { 533 Entry[] tab = table; 534 int len = tab.length; 535 536 // expunge entry at staleSlot 537 tab[staleSlot].value = null; 538 tab[staleSlot] = null; 539 size--; 540 541 // Rehash until we encounter null 542 Entry e; 543 int i; 544 for (i = nextIndex(staleSlot, len); 545 (e = tab[i]) != null; 546 i = nextIndex(i, len)) { 547 ThreadLocal k = e.get(); 548 if (k == null) { 549 e.value = null; 550 tab[i] = null; 551 size--; 552 } else { 553 int h = k.threadLocalHashCode & (len - 1); 554 if (h != i) { 555 tab[i] = null; 556 557 // Unlike Knuth 6.4 Algorithm R, we must scan until 558 // null because multiple entries could have been stale. 559 while (tab[h] != null) 560 h = nextIndex(h, len); 561 tab[h] = e; 562 } 563 } 564 } 565 return i; 566 } 567 568 /** 569 * Heuristically scan some cells looking for stale entries. 570 * This is invoked when either a new element is added, or 571 * another stale one has been expunged. It performs a 572 * logarithmic number of scans, as a balance between no 573 * scanning (fast but retains garbage) and a number of scans 574 * proportional to number of elements, that would find all 575 * garbage but would cause some insertions to take O(n) time. 576 * 577 * @param i a position known NOT to hold a stale entry. The 578 * scan starts at the element after i. 579 * 580 * @param n scan control: <tt>log2(n)</tt> cells are scanned, 581 * unless a stale entry is found, in which case 582 * <tt>log2(table.length)-1</tt> additional cells are scanned. 583 * When called from insertions, this parameter is the number 584 * of elements, but when from replaceStaleEntry, it is the 585 * table length. (Note: all this could be changed to be either 586 * more or less aggressive by weighting n instead of just 587 * using straight log n. But this version is simple, fast, and 588 * seems to work well.) 589 * 590 * @return true if any stale entries have been removed. 591 */ 592 private boolean cleanSomeSlots(int i, int n) { 593 boolean removed = false; 594 Entry[] tab = table; 595 int len = tab.length; 596 do { 597 i = nextIndex(i, len); 598 Entry e = tab[i]; 599 if (e != null && e.get() == null) { 600 n = len; 601 removed = true; 602 i = expungeStaleEntry(i); 603 } 604 } while ( (n >>>= 1) != 0); 605 return removed; 606 } 607 608 /** 609 * Re-pack and/or re-size the table. First scan the entire 610 * table removing stale entries. If this doesn't sufficiently 611 * shrink the size of the table, double the table size. 612 */ 613 private void rehash() { 614 expungeStaleEntries(); 615 616 // Use lower threshold for doubling to avoid hysteresis 617 if (size >= threshold - threshold / 4) 618 resize(); 619 } 620 621 /** 622 * Double the capacity of the table. 623 */ 624 private void resize() { 625 Entry[] oldTab = table; 626 int oldLen = oldTab.length; 627 int newLen = oldLen * 2; 628 Entry[] newTab = new Entry[newLen]; 629 int count = 0; 630 631 for (int j = 0; j < oldLen; ++j) { 632 Entry e = oldTab[j]; 633 if (e != null) { 634 ThreadLocal k = e.get(); 635 if (k == null) { 636 e.value = null; // Help the GC 637 } else { 638 int h = k.threadLocalHashCode & (newLen - 1); 639 while (newTab[h] != null) 640 h = nextIndex(h, newLen); 641 newTab[h] = e; 642 count++; 643 } 644 } 645 } 646 647 setThreshold(newLen); 648 size = count; 649 table = newTab; 650 } 651 652 /** 653 * Expunge all stale entries in the table. 654 */ 655 private void expungeStaleEntries() { 656 Entry[] tab = table; 657 int len = tab.length; 658 for (int j = 0; j < len; j++) { 659 Entry e = tab[j]; 660 if (e != null && e.get() == null) 661 expungeStaleEntry(j); 662 } 663 } 664 } 665 }
Android 4.4
1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package java.lang; 18 19 import java.lang.ref.Reference; 20 import java.lang.ref.WeakReference; 21 import java.util.concurrent.atomic.AtomicInteger; 22 23 /** 24 * Implements a thread-local storage, that is, a variable for which each thread 25 * has its own value. All threads share the same {@code ThreadLocal} object, 26 * but each sees a different value when accessing it, and changes made by one 27 * thread do not affect the other threads. The implementation supports 28 * {@code null} values. 29 * 30 * @see java.lang.Thread 31 * @author Bob Lee 32 */ 33 public class ThreadLocal<T> { 34 35 /* Thanks to Josh Bloch and Doug Lea for code reviews and impl advice. */ 36 37 /** 38 * Creates a new thread-local variable. 39 */ 40 public ThreadLocal() {} 41 42 /** 43 * Returns the value of this variable for the current thread. If an entry 44 * doesn't yet exist for this variable on this thread, this method will 45 * create an entry, populating the value with the result of 46 * {@link #initialValue()}. 47 * 48 * @return the current value of the variable for the calling thread. 49 */ 50 @SuppressWarnings("unchecked") 51 public T get() { 52 // Optimized for the fast path. 53 Thread currentThread = Thread.currentThread(); 54 Values values = values(currentThread); 55 if (values != null) { 56 Object[] table = values.table; 57 int index = hash & values.mask; 58 if (this.reference == table[index]) { 59 return (T) table[index + 1]; 60 } 61 } else { 62 values = initializeValues(currentThread); 63 } 64 65 return (T) values.getAfterMiss(this); 66 } 67 68 /** 69 * Provides the initial value of this variable for the current thread. 70 * The default implementation returns {@code null}. 71 * 72 * @return the initial value of the variable. 73 */ 74 protected T initialValue() { 75 return null; 76 } 77 78 /** 79 * Sets the value of this variable for the current thread. If set to 80 * {@code null}, the value will be set to null and the underlying entry will 81 * still be present. 82 * 83 * @param value the new value of the variable for the caller thread. 84 */ 85 public void set(T value) { 86 Thread currentThread = Thread.currentThread(); 87 Values values = values(currentThread); 88 if (values == null) { 89 values = initializeValues(currentThread); 90 } 91 values.put(this, value); 92 } 93 94 /** 95 * Removes the entry for this variable in the current thread. If this call 96 * is followed by a {@link #get()} before a {@link #set}, 97 * {@code #get()} will call {@link #initialValue()} and create a new 98 * entry with the resulting value. 99 * 100 * @since 1.5 101 */ 102 public void remove() { 103 Thread currentThread = Thread.currentThread(); 104 Values values = values(currentThread); 105 if (values != null) { 106 values.remove(this); 107 } 108 } 109 110 /** 111 * Creates Values instance for this thread and variable type. 112 */ 113 Values initializeValues(Thread current) { 114 return current.localValues = new Values(); 115 } 116 117 /** 118 * Gets Values instance for this thread and variable type. 119 */ 120 Values values(Thread current) { 121 return current.localValues; 122 } 123 124 /** Weak reference to this thread local instance. */ 125 private final Reference<ThreadLocal<T>> reference 126 = new WeakReference<ThreadLocal<T>>(this); 127 128 /** Hash counter. */ 129 private static AtomicInteger hashCounter = new AtomicInteger(0); 130 131 /** 132 * Internal hash. We deliberately don't bother with #hashCode(). 133 * Hashes must be even. This ensures that the result of 134 * (hash & (table.length - 1)) points to a key and not a value. 135 * 136 * We increment by Doug Lea's Magic Number(TM) (*2 since keys are in 137 * every other bucket) to help prevent clustering. 138 */ 139 private final int hash = hashCounter.getAndAdd(0x61c88647 * 2); 140 141 /** 142 * Per-thread map of ThreadLocal instances to values. 143 */ 144 static class Values { 145 146 /** 147 * Size must always be a power of 2. 148 */ 149 private static final int INITIAL_SIZE = 16; 150 151 /** 152 * Placeholder for deleted entries. 153 */ 154 private static final Object TOMBSTONE = new Object(); 155 156 /** 157 * Map entries. Contains alternating keys (ThreadLocal) and values. 158 * The length is always a power of 2. 159 */ 160 private Object[] table; 161 162 /** Used to turn hashes into indices. */ 163 private int mask; 164 165 /** Number of live entries. */ 166 private int size; 167 168 /** Number of tombstones. */ 169 private int tombstones; 170 171 /** Maximum number of live entries and tombstones. */ 172 private int maximumLoad; 173 174 /** Points to the next cell to clean up. */ 175 private int clean; 176 177 /** 178 * Constructs a new, empty instance. 179 */ 180 Values() { 181 initializeTable(INITIAL_SIZE); 182 this.size = 0; 183 this.tombstones = 0; 184 } 185 186 /** 187 * Used for InheritableThreadLocals. 188 */ 189 Values(Values fromParent) { 190 this.table = fromParent.table.clone(); 191 this.mask = fromParent.mask; 192 this.size = fromParent.size; 193 this.tombstones = fromParent.tombstones; 194 this.maximumLoad = fromParent.maximumLoad; 195 this.clean = fromParent.clean; 196 inheritValues(fromParent); 197 } 198 199 /** 200 * Inherits values from a parent thread. 201 */ 202 @SuppressWarnings({"unchecked"}) 203 private void inheritValues(Values fromParent) { 204 // Transfer values from parent to child thread. 205 Object[] table = this.table; 206 for (int i = table.length - 2; i >= 0; i -= 2) { 207 Object k = table[i]; 208 209 if (k == null || k == TOMBSTONE) { 210 // Skip this entry. 211 continue; 212 } 213 214 // The table can only contain null, tombstones and references. 215 Reference<InheritableThreadLocal<?>> reference 216 = (Reference<InheritableThreadLocal<?>>) k; 217 // Raw type enables us to pass in an Object below. 218 InheritableThreadLocal key = reference.get(); 219 if (key != null) { 220 // Replace value with filtered value. 221 // We should just let exceptions bubble out and tank 222 // the thread creation 223 table[i + 1] = key.childValue(fromParent.table[i + 1]); 224 } else { 225 // The key was reclaimed. 226 table[i] = TOMBSTONE; 227 table[i + 1] = null; 228 fromParent.table[i] = TOMBSTONE; 229 fromParent.table[i + 1] = null; 230 231 tombstones++; 232 fromParent.tombstones++; 233 234 size--; 235 fromParent.size--; 236 } 237 } 238 } 239 240 /** 241 * Creates a new, empty table with the given capacity. 242 */ 243 private void initializeTable(int capacity) { 244 this.table = new Object[capacity * 2]; 245 this.mask = table.length - 1; 246 this.clean = 0; 247 this.maximumLoad = capacity * 2 / 3; // 2/3 248 } 249 250 /** 251 * Cleans up after garbage-collected thread locals. 252 */ 253 private void cleanUp() { 254 if (rehash()) { 255 // If we rehashed, we needn't clean up (clean up happens as 256 // a side effect). 257 return; 258 } 259 260 if (size == 0) { 261 // No live entries == nothing to clean. 262 return; 263 } 264 265 // Clean log(table.length) entries picking up where we left off 266 // last time. 267 int index = clean; 268 Object[] table = this.table; 269 for (int counter = table.length; counter > 0; counter >>= 1, 270 index = next(index)) { 271 Object k = table[index]; 272 273 if (k == TOMBSTONE || k == null) { 274 continue; // on to next entry 275 } 276 277 // The table can only contain null, tombstones and references. 278 @SuppressWarnings("unchecked") 279 Reference<ThreadLocal<?>> reference 280 = (Reference<ThreadLocal<?>>) k; 281 if (reference.get() == null) { 282 // This thread local was reclaimed by the garbage collector. 283 table[index] = TOMBSTONE; 284 table[index + 1] = null; 285 tombstones++; 286 size--; 287 } 288 } 289 290 // Point cursor to next index. 291 clean = index; 292 } 293 294 /** 295 * Rehashes the table, expanding or contracting it as necessary. 296 * Gets rid of tombstones. Returns true if a rehash occurred. 297 * We must rehash every time we fill a null slot; we depend on the 298 * presence of null slots to end searches (otherwise, we'll infinitely 299 * loop). 300 */ 301 private boolean rehash() { 302 if (tombstones + size < maximumLoad) { 303 return false; 304 } 305 306 int capacity = table.length >> 1; 307 308 // Default to the same capacity. This will create a table of the 309 // same size and move over the live entries, analogous to a 310 // garbage collection. This should only happen if you churn a 311 // bunch of thread local garbage (removing and reinserting 312 // the same thread locals over and over will overwrite tombstones 313 // and not fill up the table). 314 int newCapacity = capacity; 315 316 if (size > (capacity >> 1)) { 317 // More than 1/2 filled w/ live entries. 318 // Double size. 319 newCapacity = capacity * 2; 320 } 321 322 Object[] oldTable = this.table; 323 324 // Allocate new table. 325 initializeTable(newCapacity); 326 327 // We won't have any tombstones after this. 328 this.tombstones = 0; 329 330 // If we have no live entries, we can quit here. 331 if (size == 0) { 332 return true; 333 } 334 335 // Move over entries. 336 for (int i = oldTable.length - 2; i >= 0; i -= 2) { 337 Object k = oldTable[i]; 338 if (k == null || k == TOMBSTONE) { 339 // Skip this entry. 340 continue; 341 } 342 343 // The table can only contain null, tombstones and references. 344 @SuppressWarnings("unchecked") 345 Reference<ThreadLocal<?>> reference 346 = (Reference<ThreadLocal<?>>) k; 347 ThreadLocal<?> key = reference.get(); 348 if (key != null) { 349 // Entry is still live. Move it over. 350 add(key, oldTable[i + 1]); 351 } else { 352 // The key was reclaimed. 353 size--; 354 } 355 } 356 357 return true; 358 } 359 360 /** 361 * Adds an entry during rehashing. Compared to put(), this method 362 * doesn't have to clean up, check for existing entries, account for 363 * tombstones, etc. 364 */ 365 void add(ThreadLocal<?> key, Object value) { 366 for (int index = key.hash & mask;; index = next(index)) { 367 Object k = table[index]; 368 if (k == null) { 369 table[index] = key.reference; 370 table[index + 1] = value; 371 return; 372 } 373 } 374 } 375 376 /** 377 * Sets entry for given ThreadLocal to given value, creating an 378 * entry if necessary. 379 */ 380 void put(ThreadLocal<?> key, Object value) { 381 cleanUp(); 382 383 // Keep track of first tombstone. That's where we want to go back 384 // and add an entry if necessary. 385 int firstTombstone = -1; 386 387 for (int index = key.hash & mask;; index = next(index)) { 388 Object k = table[index]; 389 390 if (k == key.reference) { 391 // Replace existing entry. 392 table[index + 1] = value; 393 return; 394 } 395 396 if (k == null) { 397 if (firstTombstone == -1) { 398 // Fill in null slot. 399 table[index] = key.reference; 400 table[index + 1] = value; 401 size++; 402 return; 403 } 404 405 // Go back and replace first tombstone. 406 table[firstTombstone] = key.reference; 407 table[firstTombstone + 1] = value; 408 tombstones--; 409 size++; 410 return; 411 } 412 413 // Remember first tombstone. 414 if (firstTombstone == -1 && k == TOMBSTONE) { 415 firstTombstone = index; 416 } 417 } 418 } 419 420 /** 421 * Gets value for given ThreadLocal after not finding it in the first 422 * slot. 423 */ 424 Object getAfterMiss(ThreadLocal<?> key) { 425 Object[] table = this.table; 426 int index = key.hash & mask; 427 428 // If the first slot is empty, the search is over. 429 if (table[index] == null) { 430 Object value = key.initialValue(); 431 432 // If the table is still the same and the slot is still empty... 433 if (this.table == table && table[index] == null) { 434 table[index] = key.reference; 435 table[index + 1] = value; 436 size++; 437 438 cleanUp(); 439 return value; 440 } 441 442 // The table changed during initialValue(). 443 put(key, value); 444 return value; 445 } 446 447 // Keep track of first tombstone. That's where we want to go back 448 // and add an entry if necessary. 449 int firstTombstone = -1; 450 451 // Continue search. 452 for (index = next(index);; index = next(index)) { 453 Object reference = table[index]; 454 if (reference == key.reference) { 455 return table[index + 1]; 456 } 457 458 // If no entry was found... 459 if (reference == null) { 460 Object value = key.initialValue(); 461 462 // If the table is still the same... 463 if (this.table == table) { 464 // If we passed a tombstone and that slot still 465 // contains a tombstone... 466 if (firstTombstone > -1 467 && table[firstTombstone] == TOMBSTONE) { 468 table[firstTombstone] = key.reference; 469 table[firstTombstone + 1] = value; 470 tombstones--; 471 size++; 472 473 // No need to clean up here. We aren't filling 474 // in a null slot. 475 return value; 476 } 477 478 // If this slot is still empty... 479 if (table[index] == null) { 480 table[index] = key.reference; 481 table[index + 1] = value; 482 size++; 483 484 cleanUp(); 485 return value; 486 } 487 } 488 489 // The table changed during initialValue(). 490 put(key, value); 491 return value; 492 } 493 494 if (firstTombstone == -1 && reference == TOMBSTONE) { 495 // Keep track of this tombstone so we can overwrite it. 496 firstTombstone = index; 497 } 498 } 499 } 500 501 /** 502 * Removes entry for the given ThreadLocal. 503 */ 504 void remove(ThreadLocal<?> key) { 505 cleanUp(); 506 507 for (int index = key.hash & mask;; index = next(index)) { 508 Object reference = table[index]; 509 510 if (reference == key.reference) { 511 // Success! 512 table[index] = TOMBSTONE; 513 table[index + 1] = null; 514 tombstones++; 515 size--; 516 return; 517 } 518 519 if (reference == null) { 520 // No entry found. 521 return; 522 } 523 } 524 } 525 526 /** 527 * Gets the next index. If we're at the end of the table, we wrap back 528 * around to 0. 529 */ 530 private int next(int index) { 531 return (index + 2) & mask; 532 } 533 } 534 }
简单的看一下Java源码,我们就会发现,你set一个对象到ThreadLocal的时候,其实这个对象是被保存到当前线程的ThreadLocalMap里的,而每一个Thread里都包含一个ThreadLocal.ThreadLocalMap threadLocals = null;代码:
1 public void set(T value) { 2 Thread t = Thread.currentThread(); 3 ThreadLocalMap map = getMap(t); 4 if (map != null) 5 map.set(this, value); 6 else 7 createMap(t, value); 8 }
1 ThreadLocalMap getMap(Thread t) { 2 return t.threadLocals; 3 }
1 public 2 class Thread implements Runnable { 3 /* Make sure registerNatives is the first thing <clinit> does. */ 4 private static native void registerNatives(); 5 static { 6 registerNatives(); 7 } 8 9 private char name[]; 10 private int priority; 11 private Thread threadQ; 12 private long eetop; 13 ... 14 /* ThreadLocal values pertaining to this thread. This map is maintained 15 * by the ThreadLocal class. */ 16 ThreadLocal.ThreadLocalMap threadLocals = null;
重点看这句:
1 map.set(this, value);
可能有人要问了,如果只是简单的将value放到Map对象里面,那不可能起到每个线程各自保存一份对象的目的啊,我们继续看map.set(this, value)方法:
1 /** 2 * Set the value associated with key. 3 * 4 * @param key the thread local object 5 * @param value the value to be set 6 */ 7 private void set(ThreadLocal key, Object value) { 8 9 // We don't use a fast path as with get() because it is at 10 // least as common to use set() to create new entries as 11 // it is to replace existing ones, in which case, a fast 12 // path would fail more often than not. 13 14 Entry[] tab = table; 15 int len = tab.length; 16 int i = key.threadLocalHashCode & (len-1); 17 18 for (Entry e = tab[i]; 19 e != null; 20 e = tab[i = nextIndex(i, len)]) { 21 ThreadLocal k = e.get(); 22 23 if (k == key) { 24 e.value = value; 25 return; 26 } 27 28 if (k == null) { 29 replaceStaleEntry(key, value, i); 30 return; 31 } 32 } 33 34 tab[i] = new Entry(key, value); 35 int sz = ++size; 36 if (!cleanSomeSlots(i, sz) && sz >= threshold) 37 rehash(); 38 }
看到了吧,不是简单的值传递,而是重新创建了一个对象并保存起来。然后get的时候当然就是线程自己创建的对象喽,其实实现原理很简单,不是吗?