Java中的ThreadLocal 可以看做以线程标识为key的Map,在多线程开发中使用非常方便。
示例
1 class ThreadEnv { 2 3 // 用匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值 4 private static ThreadLocalthreadId = new ThreadLocal () { 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] >>> 11Thread[Thread-1,5,main] >>> 11Thread[Thread-0,5,main] >>> 11Thread[Thread-1,5,main] >>> 12Thread[Thread-1,5,main] >>> 13Thread[Thread-2,5,main] >>> 12Thread[Thread-0,5,main] >>> 12Thread[Thread-0,5,main] >>> 13Thread[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 thread105 * @return a map containing the parent's inheritable bindings106 */107 static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {108 return new ThreadLocalMap(parentMap);109 }110 111 /**112 * Method childValue is visibly defined in subclass113 * InheritableThreadLocal, but is internally defined here for the114 * sake of providing createInheritedMap factory method without115 * needing to subclass the map class in InheritableThreadLocal.116 * This technique is preferable to the alternative of embedding117 * 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 from125 * the specified { @code Supplier}.126 */127 static final class SuppliedThreadLocalextends ThreadLocal {128 129 private final Supplier supplier;130 131 SuppliedThreadLocal(Supplier supplier) {132 this.supplier = Objects.requireNonNull(supplier);133 }134 135 @Override136 protected T initialValue() {137 return supplier.get();138 }139 }140 141 /**142 * ThreadLocalMap is a customized hash map suitable only for143 * maintaining thread local values. No operations are exported144 * outside of the ThreadLocal class. The class is package private to145 * allow declaration of fields in class Thread. To help deal with146 * very large and long-lived usages, the hash table entries use147 * WeakReferences for keys. However, since reference queues are not148 * used, stale entries are guaranteed to be removed only when149 * the table starts running out of space.150 */151 static class ThreadLocalMap {152 153 /**154 * The entries in this hash map extend WeakReference, using155 * its main ref field as the key (which is always a156 * ThreadLocal object). Note that null keys (i.e. entry.get()157 * == null) mean that the key is no longer referenced, so the158 * entry can be expunged from table. Such entries are referred to159 * as "stale entries" in the code that follows.160 */161 static class Entry extends WeakReference > {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 0191 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 create216 * 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 ThreadLocals228 * 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