ThreadLocal是java多线程编程中非常重要的一个类。ThreadLocal存放的变量只能由相应的线程进行访问,即ThreadLocal为每个线程存放一个变量的副本。 下面对ThreadLocal进行源码分析
查看Thread类的源代码,可以看到Thread类中有个
ThreadLocal.ThreadLocalMap threadLocals = null;此变量没有声明为private,而是使用了默认的类型friendly,即同一个包下下的类可以看到。 其实 线程本地变量就存储在threadLocals map中。
由于Key为WeakReference,因此当key被垃圾回收时,需要把entry从table中移除。并重新rehash整个数组 nextIndex函数的定义如下:
private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); }在重新rehash的过程中,如果遇到key==null,则把value设置为null。 否则如果key != null,则hash,如果对应的hashcode !==i,则把tab[i] = null,并线性探测tab[h],直到tab[h]==null,并把此元素放入到tab[h]中。
private int expungeStaleEntry(int staleSlot) { Entry[] tab = table; int len = tab.length; // expunge entry at staleSlot tab[staleSlot].value = null; tab[staleSlot] = null; size--; // Rehash until we encounter null Entry e; int i; for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal<?> k = e.get(); if (k == null) { e.value = null; tab[i] = null; size--; } else { int h = k.threadLocalHashCode & (len - 1); if (h != i) { tab[i] = null; while (tab[h] != null) h = nextIndex(h, len); tab[h] = e; } } } return i; }InheritableThreadLocal可以让新开的线程拿到父线程的值:
public class InheritableThreadLocal<T> extends ThreadLocal<T> { protected T childValue(T parentValue) { return parentValue; } ThreadLocalMap getMap(Thread t) { return t.inheritableThreadLocals; } void createMap(Thread t, T firstValue) { t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue); } }InheritableThreadLocal 继承ThreadLocal类
由于InheritableThreadLocal没有覆盖ThreadLocal的get方法。因此,调用的是父类的get()方法:
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }这里会有一个Thread.currentThread() ,getMap(t) 方法,所以就会得到这个线程 threadlocals。
但是,由于子类 InheritableThreadLocal 重写了 getMap()方法,再看上述代码。 我们可以看到:其实不是得到 threadlocals,而是得到inheritableThreadLocals。
inheritableThreadLocals 之前一直没提及过,其实它也是 Thread 类的一个 ThreadLocalMap 类型的 属性,如下 Thread 类的部分代码:
ThreadLocal.ThreadLocalMap threadLocals = null; ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;init方法代码:
private void init(ThreadGroup g, Runnable target, String name, long stackSize) { init(g, target, name, stackSize, null); } private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) { ...... if (parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); /* Stash the specified stack size in case the VM cares */ this.stackSize = stackSize; ...... }hreadLocal.createInheritedMap(parent.inheritableThreadLocals)实现:
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) { return new ThreadLocalMap(parentMap); } 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) { @SuppressWarnings("unchecked") ThreadLocal<Object> key = (ThreadLocal<Object>) 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++; } } } }当我们创建一个新的线程的时候X,X线程就会有 ThreadLocalMap 类型的 inheritableThreadLocals ,因为它是 Thread 类的一个属性。
然后
先得到当前线程存储的这些值,例如 Entry[] parentTable = parentMap.table; 。再通过一个 for 循环,不断的把当前线程的这些值复制到我们新创建的线程X 的inheritableThreadLocals 中。就这样,就ok了。
