阅读总结:
ThreadLocal内部使用静态map存储,每个变量对应一个hashcode,不需要指定key值,后台动态生成,good!
每个变量ThreadLocal内部分配Entry,获取值时,通过变量找到Entry,找到对应hashcode,获取值;
设置值同理。
init部分,有点晕忽,写的乱七八糟,查了下源代码,其实就是线程初始化的时候,新建了个ThreadLocalMap变量,和什么子线程父线程木有任何关系。
在阅读《Java Concurrency In Practice》时,书中提到ThreadLocal是一种更为规范常用的Thread Confine方式。于是想仔细分析一下ThreadLocal的实现方式。曾经转载了一篇关于ThreadLocal的文章:,其中提到ThreadLocal的实现方式是声明一个Hashtable,然后以Thread.currentThread()为key,变量的拷贝为value。今天阅读源码才知道实现方式已经大为改变,下面来看代码。
/*** 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只有三个变量,从构造函数知道,在创建一个ThreadLocal实例时,只是调用nextHashCode方法将nextHashCode的值赋给实例的threadLocalHashCode,然后nextHashCode的值增加HASH_INCREMENT这个值。 因此ThreadLocal实例的变量只有threadLocalHashCode,而且是final的,用来区分不同的ThreadLocal实例。再来看其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是内部静态类,部分代码如下:/*** 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;Entry(ThreadLocal k, Object v) { super(k);value = v;}}/*** 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 0Entry继承WeakReference,通过其注释并结合WeakReference的功能,我们知道:一旦没有指向 key 的强引用, ThreadLocalMap 在 GC 后将自动删除相关的 entry。ThreadLocalMap采用数组来保存Entry,并且Entry中以ThreadLocal为key,初始大小为16. 接着看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有两个构造函数,可以直接传入ThreadLcoal-value对,也可以传入一个ThreadLocalMap,传入ThreadLocalMap的时候,会依次将其Entry存放在table中。接着来分析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;elsereturn getEntryAfterMiss(key, i, e);}通过实例变量threadLocalHashCode算出下标,然后返回其值。set和remove方法类似。继续看ThreadLocal类的get方法,通过getMap(Thread t)返回ThreadLocalMap,然后从ThreadLocalMap中通过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;}可以看出其返回的是线程的一个实例变量。由此可知Thread类也持有ThreadLocalMap,这样每个线程的变量都存放在自己的ThreadLocalMap中,可谓名符其实。继续看Thread类如何操作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类中声明了两个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();elsethis.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方法中,存在着对inheritableThreadLocals的操作:if (parent.inheritableThreadLocals != null)this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);而ThreadLocal的createInheritedMap方法则是调用ThreadLocalMap类传入ThreadLocalMap参数的构造函数。也就是说在Thread类中,当前线程会调用init方法去初始一个线程,而在init方法中,会将当前线程的inheritableThreadLocals拷贝给等待初始化的线程。这让我联想起unix/linux系统中,父线程会调用fork()函数生成一个子线程,而且会把父线程大部分的信息拷贝给子线程。 最后来看Thread类的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;}在线程真正终止前会执行这个方法,这个方法会把threadLocals和inheritableThreadLocals指向null。但我在Thread类中并没有看到对threadLocals的赋值,应该是通过ThreadLocal来设置的。 写了个简单的Thread测试程序,只是想跟踪一下上述两个ThreadLocalMap变量的状态: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");}}可以看到,启动一个线程,不停打印系统时间,然后通过ThreadLocal给当前线程添加一份字符串,观察有: inheritableThreadLocals中有一个Entry,但value为null,threadLocals中有三个Entry,其中两个value不明,一个为ThreadLocal设置的值。不过我实在不知道其他三个Entry值是如何设置的,留个疑问。总结:ThreadLocal实例只有一个threadLocalHashCode值,ThreadLocal给各个线程设置的值都是存在各个线程threadLocals里 。相比Hashtable的实现方式,现在的方式更为合理。当一个线程终止时,其inheritableThreadLocals和threadLocals均被置为null,于是通过TreadLocal也就无法访问这个线程;而当ThreadLocal被设置为null时,Thread里threadLocals就会移除key为ThreadLocal的Entry。Hashtable的实现方式则无法实现这一点。最为关键的是Hashtable的实现需要同步,所带来的性能损耗是很大的,而现在的方式则不需要同步。性能提升很大。