在上一篇中具体讨论了ThreadLocal的源码及ThreadLocalMap的核心代码。还有一个相对没那么重要的内容没有讨论,那就是 InheritableThreadLocal。InheritableThreadLocal是ThreadLocal的子类,当父线程创建一个InheritableThreadLocal对象之后,InheritableThreadLocal的内容能够在这个父线程的所有子线程中共享。这个实现相当有意义。比如可以利用这个类实现多线程的共享事务。这个类实际上非常简单,在Thread源码中对其做了支持。
有如下测试类,在main函数的主线程中给InheritableThreadLocal设置了一个字符串。之后在这个主线程的各个子线程中进行读取。
public class InheritableThreadLocalTest {
private static final InheritableThreadLocal<String> itl = new InheritableThreadLocal<>();
public static void main(String[] args) {
itl.set("主线程赋值");
System.out.println("value:"+itl.get());
new Thread(() -> {
System.out.println("子线程1获取:"+itl.get());
}).start();
new Thread(() -> {
System.out.println("子线程2获取:"+itl.get());
}).start();
}
}
上面代码执行结果如下:
value:主线程赋值
子线程1获取:主线程赋值
子线程2获取:主线程赋值
Process finished with exit code 0
InheritableThreadLocal源码非常简单,继承了ThreadLocal,因此其本质就是一个ThreadLocal。只是将三个方法进行了重写,childValue、getMap和createMap。
/**
* This class extends <tt>ThreadLocal</tt> to provide inheritance of values
* from parent thread to child thread: when a child thread is created, the
* child receives initial values for all inheritable thread-local variables
* for which the parent has values. Normally the child's values will be
* identical to the parent's; however, the child's value can be made an
* arbitrary function of the parent's by overriding the <tt>childValue</tt>
* method in this class.
*
* <p>Inheritable thread-local variables are used in preference to
* ordinary thread-local variables when the per-thread-attribute being
* maintained in the variable (e.g., User ID, Transaction ID) must be
* automatically transmitted to any child threads that are created.
*
* @author Josh Bloch and Doug Lea
* @see ThreadLocal
* @since 1.2
*/
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
/**
* Computes the child's initial value for this inheritable thread-local
* variable as a function of the parent's value at the time the child
* thread is created. This method is called from within the parent
* thread before the child is started.
* <p>
* This method merely returns its input argument, and should be overridden
* if a different behavior is desired.
*
* @param parentValue the parent thread's value
* @return the child thread's initial value
*/
protected T childValue(T parentValue) {
return parentValue;
}
/**
* Get the map associated with a ThreadLocal.
*
* @param t the current thread
*/
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
/**
* Create the map associated with a ThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the table.
*/
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
在分析ThreadLocal的代码中,set和get的方法中,都有getMap和createMap。这两个方法重写了之后,就将之前的从Thread中的threadLoccals获取threadLocalMap变成了从inheritableThreadLocals获取ThreadLocalMap。 在看完这些代码之后,还是没有明白,是如何将ThreadLocalMap的内容放置到Thread的inheritableThreadLocals变量的。 因此需要进一步对Thread代码进行分析。
在Thread源码的init方法中,可以看到:
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
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 (name == null) {
throw new NullPointerException("name cannot be null");
}
... ...
//与本文最关键的部分
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}
可以看到,实际上在创建Thread的时候,会从父线程中去判断inheritableThreadLocals 是否为空,如果不为空,则调用ThreadLocal的createInheriteMap方法。
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
而这也解决了在之前分析ThreadLocal源码中的一个疑问。之前在ThreadLocal的初始化方法中:
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++;
}
}
}
}
上面的代码要用到 key.childValue(e.value);。这个方法。而这个方法在ThreadLocal中:
T childValue(T parentValue) {
throw new UnsupportedOperationException();
}
这个方法没有返回值,只会抛出一个异常。当时的备注页明确说明了:
Construct a new map including all Inheritable ThreadLocals
from given parent map. Called only by createInheritedMap.
这个批量创建ThreadLocalMap的方法只能用在createInheritedMap的时候,否则就会抛出异常。而使用createInheritedMap的时候,子类重写了childValue方法:
protected T childValue(T parentValue) {
return parentValue;
}
这样就不会抛出异常。这也是面向对象多态特性的一种 具体的应用。虽然其设计不如ThreadLocalMap及WeakReference方法那么让人耳目一新,但是也是我们自己在做架构设计的时候值得借鉴的。
1.InheritableThreadLocal在线程创建的时候,从父线程中拷贝了inheritableThreadLocals,这是一个相对的深度拷贝,重建了整个ThreadLocalMap。如果Entry的value不是引用类型,那么这些Entry的值在每个Thread中互不影响。由于只copy到Entry这一级,如果Entry的value本身就是引用类型,那么将会共享。 2.InheritableThreadLocal利用了面向对象的多态特性,重写了childValue、getMap和createMap方法。在Thread中对inheritableThreadLocals进行了处理。这说明,如果在Thread的基础上实现共享内存或者事务等,只能使用ThreadLocal或者InheritableThreadLocal来实现。 3.InheritableThreadLocal与ThreadLocal会有相同的内存泄漏的风险。因此需要注意对remove方法的使用。避免导致OOM或者内存泄漏。