ThreadLocal 及InheritableThreadLocal 源码分析

xiaoxiao2021-02-28  71

ThreadLocal


ThreadLocal是java多线程编程中非常重要的一个类。ThreadLocal存放的变量只能由相应的线程进行访问,即ThreadLocal为每个线程存放一个变量的副本。 下面对ThreadLocal进行源码分析

Thread 类

查看Thread类的源代码,可以看到Thread类中有个

ThreadLocal.ThreadLocalMap threadLocals = null;

此变量没有声明为private,而是使用了默认的类型friendly,即同一个包下下的类可以看到。 其实 线程本地变量就存储在threadLocals map中。

ThreadLocal 类

public class ThreadLocal<T> { /** * ThreadLocals 解决Hash 冲突使用线性探测的方法。 * 其中key为ThreadLocal对象。因为Thread类中的 threadLocals是个map对象。 * 意外这一个Thread中可以存放多个ThreadLocal对象。所以Key为ThreadLocal,value为需要存放的值。 * 把threadLocalHashCode声明为final,所以可以作为 ThreadLocalMap中的key,保持不变。 * 调用nextHashCode()使得每次把共享的类变量值+1; */ private final int threadLocalHashCode = nextHashCode(); /** * nextHashCode声明为static,代表类变量。默认初始值为 0。 * 使用原子整数类作为下一个hashcode值。由于是个类变量,所以当jvm把ThreadLocal加载到内存中时。 * nextHashCode只有一份,是线程共享的。 */ private static AtomicInteger nextHashCode = new AtomicInteger(); /** *由于采用线性探测,所以代表下一个值在当前值得hash增长。 */ private static final int HASH_INCREMENT = 0x61c88647; /** * 返回一下hashcode */ private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); }

initialValue()方法

/** *这个方法返回当前线程threadLocal的初始变量值。 *这个方法的调用出现在ThreadLocal调用get()方法,然后之前没有调用ThreadLocal的set()方法。 *这个方法被当前线程调用一次或者调用多次。当调用ThreadLocal的remove()方法后,可能会再次调用此方法。 *这个方法默认返回Null,如果程序员想让它返回一个初始值,通过一个匿名内部类重写此方法。 *如: ThreadLocal<Integer> local = new ThreadLocal<Integer>(){ @Override protected Integer initialValue() { return 0; } }; */ protected T initialValue() { return null; }

get()方法

/** * 返回当前线程的threadLocal变量的拷贝值。 * 如果这个变量对当前线程没有值,则它调用前面介绍的initialValue方法获取初始值。 */ public T get() { Thread t = Thread.currentThread(); //依据当前线程获取ThreadLocalMap /** * 获取Thread对象的threadLocals属性 * ThreadLocalMap getMap(Thread t) { return t.threadLocals; } */ ThreadLocalMap map = getMap(t); if (map != null) { /** *由于一个thread对象可以存放多个ThreadLocal变量,因此使用ThreadLocal作为key值。 */ ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }

ThreadLocalMap数据结构

static class ThreadLocalMap { /** * 这个entry继承WeakReference,为了避免当内存不够用被回收。 * 注意null keys(如:entry.get()==null)意外这这个Key不再引用,因此这个entry可以从这个表中被移除。 * */ static class Entry extends WeakReference<ThreadLocal<?>> { Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } /** * table的初始化值,必须是2的幂。默认是16 */ private static final int INITIAL_CAPACITY = 16; /** * 这个表可以动态扩展 * 表的长度必须是2的幂 */ private Entry[] table; /** * 表中的元素的数量. */ private int size = 0; /** * 当size大于threshode时,就需要进行扩容。类似于加载因子 */ private int threshold; // Default to 0 /** * threadHold的设置值方法 */ private void setThreshold(int len) { threshold = len * 2 / 3; } ...
getEntry(ThreadLocal key ) 方法
private Entry getEntry(ThreadLocal<?> key) { //获取hash 值code 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); } private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) { Entry[] tab = table; int len = tab.length; while (e != null) { ThreadLocal<?> k = e.get(); //由于key为this对象。因此采用比较内存地址的方式 if (k == key) return e; //如果key为null,则引用被垃圾回收,需要从表里删除 if (k == null) expungeStaleEntry(i); else //把i值加1再做判断,直到找到对应的key值 i = nextIndex(i, len); e = tab[i]; } return null; }

expungeStaleEntry()源码分析

由于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


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类

get()方法实现

由于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;

在创建新线程时往InheritaleThreadLocals中写入值

public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); }

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了。

转载请注明原文地址: https://www.6miu.com/read-42053.html

最新回复(0)