在閱讀《Java Concurrency In Practice》時(shí),書(shū)中提到ThreadLocal是一種更為規范常用的Thread Confine方式。于是想仔細分析一下ThreadLocal的實(shí)現方式。曾經(jīng)轉載了一篇關(guān)于ThreadLocal的文章:hi.baidu.com/gefforey520/blog/item/c3bb64fa4ad1779358ee902c.html,其中提到ThreadLocal的實(shí)現方式是聲明一個(gè)Hashtable,然后以Thread.currentThread()為key,變量的拷貝為value。今天閱讀源碼才知道實(shí)現方式已經(jīng)大為改變,下面來(lái)看代碼。 /** * ThreadLocals rely on per-thread linear-probe hash maps attached to each * thread (Thread.threadLocals and inheritableThreadLocals). The ThreadLocal * objects act as keys, searched via threadLocalHashCode. This is a custom * hash code (useful only within ThreadLocalMaps) that eliminates collisions * in the common case where consecutively constructed ThreadLocals are used * by the same threads, while remaining well-behaved in less common cases. */ private final int threadLocalHashCode = nextHashCode();
/** * The next hash code to be given out. Updated atomically. Starts at zero. */ private static AtomicInteger nextHashCode = new AtomicInteger();
/** * The difference between successively generated hash codes - turns implicit * sequential thread-local IDs into near-optimally spread multiplicative * hash values for power-of-two-sized tables. */ private static final int HASH_INCREMENT = 0x61c88647;
/** * Returns the next hash code. */ private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); } /** * Creates a thread local variable. */ public ThreadLocal() { } ThreadLocal只有三個(gè)變量,從構造函數知道,在創(chuàng )建一個(gè)ThreadLocal實(shí)例時(shí),只是調用nextHashCode方法將nextHashCode的值賦給實(shí)例的threadLocalHashCode,然后nextHashCode的值增加HASH_INCREMENT這個(gè)值。 因此ThreadLocal實(shí)例的變量只有threadLocalHashCode,而且是final的,用來(lái)區分不同的ThreadLocal實(shí)例。 再來(lái)看其get方法: /** * Returns the value in the current thread's copy of this thread-local * variable. If the variable has no value for the current thread, it is * first initialized to the value returned by an invocation of the * {@link #initialValue} method. * * @return the current thread's value of this thread-local */ public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T) e.value; } return setInitialValue(); } 其中調用getMap(Thread t)返回ThreadLocalMap,ThreadLocalMap是內部靜態(tài)類(lèi),部分代碼如下: /** * ThreadLocalMap is a customized hash map suitable only for maintaining * thread local values. No operations are exported outside of the * ThreadLocal class. The class is package private to allow declaration of * fields in class Thread. To help deal with very large and long-lived * usages, the hash table entries use WeakReferences for keys. However, * since reference queues are not used, stale entries are guaranteed to be * removed only when the table starts running out of space. */ static class ThreadLocalMap {
/** * The entries in this hash map extend WeakReference, using its main ref * field as the key (which is always a ThreadLocal object). Note that * null keys (i.e. entry.get() == null) mean that the key is no longer * referenced, so the entry can be expunged from table. Such entries are * referred to as "stale entries" in the code that follows. */ static class Entry extends WeakReference<ThreadLocal> { /** The value associated with this ThreadLocal. */ Object value;
/** * The initial capacity -- MUST be a power of two. */ private static final int INITIAL_CAPACITY = 16;
/** * The table, resized as necessary. table.length MUST always be a power * of two. */ private Entry[] table;
/** * The number of entries in the table. */ private int size = 0;
/** * The next size value at which to resize. */ private int threshold; // Default to 0 Entry繼承WeakReference,通過(guò)其注釋并結合WeakReference的功能,我們知道:一旦沒(méi)有指向 key 的強引用, ThreadLocalMap 在 GC 后將自動(dòng)刪除相關(guān)的 entry。ThreadLocalMap采用數組來(lái)保存Entry,并且Entry中以ThreadLocal為key,初始大小為16. 接著(zhù)看ThreadLocalMap的constructor: /** * 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); } /** * Construct a new map including all Inheritable ThreadLocals from given * parent map. Called only by createInheritedMap. * * @param parentMap * the map associated with parent thread. */ private ThreadLocalMap(ThreadLocalMap parentMap) { Entry[] parentTable = parentMap.table; int len = parentTable.length; setThreshold(len); table = new Entry[len];
for (int j = 0; j < len; j++) { Entry e = parentTable[j]; if (e != null) { ThreadLocal key = e.get(); if (key != null) { Object value = key.childValue(e.value); Entry c = new Entry(key, value); int h = key.threadLocalHashCode & (len - 1); while (table[h] != null) h = nextIndex(h, len); table[h] = c; size++; } } } } ThreadLocalMap有兩個(gè)構造函數,可以直接傳入ThreadLcoal-value對,也可以傳入一個(gè)ThreadLocalMap,傳入ThreadLocalMap的時(shí)候,會(huì )依次將其Entry存放在table中。接著(zhù)來(lái)分析get方法: /** * Get the entry associated with key. This method itself handles only * the fast path: a direct hit of existing key. It otherwise relays to * getEntryAfterMiss. This is designed to maximize performance for * direct hits, in part by making this method readily inlinable. * * @param key * the thread local object * @return the entry associated with key, or null if no such */ private Entry getEntry(ThreadLocal key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e); } 通過(guò)實(shí)例變量threadLocalHashCode算出下標,然后返回其值。set和remove方法類(lèi)似。 繼續看ThreadLocal類(lèi)的get方法,通過(guò)getMap(Thread t)返回ThreadLocalMap,然后從ThreadLocalMap中通過(guò)getEntry(ThreadLocal key) 取出值。下面繼續看getMap(Thread t)方法: /** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t * the current thread * @return the map */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; } 可以看出其返回的是線(xiàn)程的一個(gè)實(shí)例變量。由此可知Thread類(lèi)也持有ThreadLocalMap,這樣每個(gè)線(xiàn)程的變量都存放在自己的ThreadLocalMap中,可謂名符其實(shí)。 繼續看Thread類(lèi)如何操作ThreadLocalMap: /* * ThreadLocal values pertaining to this thread. This map is maintained by * the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null;
/* * InheritableThreadLocal values pertaining to this thread. This map is * maintained by the InheritableThreadLocal class. */ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; Thread類(lèi)中聲明了兩個(gè)ThreadLocalMap變量, /** * Initializes a Thread. * * @param g * the Thread group * @param target * the object whose run() method gets called * @param name * the name of the new Thread * @param stackSize * the desired stack size for the new thread, or zero to indicate * that this parameter is to be ignored. */ private void init(ThreadGroup g, Runnable target, String name, long stackSize) { Thread parent = currentThread(); SecurityManager security = System.getSecurityManager(); if (g == null) { /* Determine if it's an applet or not */
/* * If there is a security manager, ask the security manager what to * do. */ if (security != null) { g = security.getThreadGroup(); }
/* * If the security doesn't have a strong opinion of the matter use * the parent thread group. */ if (g == null) { g = parent.getThreadGroup(); } }
/* * checkAccess regardless of whether or not threadgroup is explicitly * passed in. */ g.checkAccess();
/* * Do we have the required permissions? */ if (security != null) { if (isCCLOverridden(getClass())) { security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); } }
g.addUnstarted();
this.group = g; this.daemon = parent.isDaemon(); this.priority = parent.getPriority(); this.name = name.toCharArray(); if (security == null || isCCLOverridden(parent.getClass())) this.contextClassLoader = parent.getContextClassLoader(); else this.contextClassLoader = parent.contextClassLoader; this.inheritedAccessControlContext = AccessController.getContext(); this.target = target; setPriority(priority); if (parent.inheritableThreadLocals != null) this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); /* Stash the specified stack size in case the VM cares */ this.stackSize = stackSize;
/* Set thread ID */ tid = nextThreadID(); } 在init方法中,存在著(zhù)對inheritableThreadLocals的操作: if (parent.inheritableThreadLocals != null) this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); 而ThreadLocal的createInheritedMap方法則是調用ThreadLocalMap類(lèi)傳入ThreadLocalMap參數的構造函數。 也就是說(shuō)在Thread類(lèi)中,當前線(xiàn)程會(huì )調用init方法去初始一個(gè)線(xiàn)程,而在init方法中,會(huì )將當前線(xiàn)程的inheritableThreadLocals拷貝給等待初始化的線(xiàn)程。這讓我聯(lián)想起unix/linux系統中,父線(xiàn)程會(huì )調用fork()函數生成一個(gè)子線(xiàn)程,而且會(huì )把父線(xiàn)程大部分的信息拷貝給子線(xiàn)程。 最后來(lái)看Thread類(lèi)的exit方法: /** * This method is called by the system to give a Thread a chance to clean up * before it actually exits. */ private void exit() { if (group != null) { group.remove(this); group = null; } /* Aggressively null out all reference fields: see bug 4006245 */ target = null; /* Speed the release of some of these resources */ threadLocals = null; inheritableThreadLocals = null; inheritedAccessControlContext = null; blocker = null; uncaughtExceptionHandler = null; } 在線(xiàn)程真正終止前會(huì )執行這個(gè)方法,這個(gè)方法會(huì )把threadLocals和inheritableThreadLocals指向null。但我在Thread類(lèi)中并沒(méi)有看到對threadLocals的賦值,應該是通過(guò)ThreadLocal來(lái)設置的。 寫(xiě)了個(gè)簡(jiǎn)單的Thread測試程序,只是想跟蹤一下上述兩個(gè)ThreadLocalMap變量的狀態(tài): public class TimePrinter extends Thread {
public void run() { while (true) { try { System.out.println(new Date(System.currentTimeMillis())); } catch (Exception e) { System.out.println(e); } } }
static public void main(String args[]) { TimePrinter tp1 = new TimePrinter(); tp1.start(); ThreadLocal t2 = new ThreadLocal(); t2.set("aaaaaaaaaaaaaaaaaaaaaaaa"); } } 可以看到,啟動(dòng)一個(gè)線(xiàn)程,不停打印系統時(shí)間,然后通過(guò)ThreadLocal給當前線(xiàn)程添加一份字符串,觀(guān)察有: