3 InheritableThreadLocal的使用
通过上面的分析知道通过ThreadLocal保存的值是线程隔离的。其实在Thread对象中,还有一个ThreadLocal.ThreadLocalMap类型的成员变量inheritableThreadLocals。inheritableThreadLocals其实是一个InheritableThreadLocals类型,InheritableThreadLocals是ThreadLocal的子类。保存在inheritableThreadLocals中的值可以传递给子线程。下面看一个例子:
[java] view plain copy
-
public class InheritableThreadLocalTest {
-
static InheritableThreadLocal<Long> mInheritableThreadLocal = new InheritableThreadLocal<Long>();
-
static ThreadLocal<Long> mThreadLocal = new ThreadLocal<Long>();
-
static SimpleInheritableThreadLocal<Long> mSimpleInheritableThreadLocal=new SimpleInheritableThreadLocal<Long>();
-
public static void printValue() {
-
-
System.out.println("Thread " + Thread.currentThread().getId() + ": valueFromParent=" + mInheritableThreadLocal.get() + " valueFromLocal="
-
+ mThreadLocal.get() +" simpleValueFromParent="+mSimpleInheritableThreadLocal.get());
-
}
-
-
static class PrintValueRunnable implements Runnable {
-
-
@Override
-
public void run() {
-
// TODO Auto-generated method stub
-
InheritableThreadLocalTest.printValue();
-
if (Thread.currentThread().getId() % 2 == 0) {
-
mInheritableThreadLocal.set(mInheritableThreadLocal.get()+1);
-
}
-
InheritableThreadLocalTest.printValue();
-
}
-
}
-
-
public static void main(String[] args) {
-
-
long tid = Thread.currentThread().getId();
-
mInheritableThreadLocal.set(tid);
-
mThreadLocal.set(tid);
-
mSimpleInheritableThreadLocal.set(tid);
-
System.out.println("mainThread: " + " valueFromLocal=" + mThreadLocal.get());
-
new Thread(new PrintValueRunnable()).start();
-
new Thread(new PrintValueRunnable()).start();
-
new Thread(new PrintValueRunnable()).start();
-
}
-
}
打印结果如下:
在这个例子,先在主线程中new一个InheritableThreadLocal对象,然后在子线程中访问。通过打印结果可以看到,主线程设置到InheritableThreadLocal中的值会复制给子线程,然后父子线程可以各自独立读写保存在InheritableThreadLocal中的值。可以还有人注意到了,在上面的例子中还有一个SimpleInheritableThreadLocal对象,这个是我自定义,它继承了InheritableThreadLocal,并重写了InheritableThreadLocal.childValue。我们可以通过重写childValue来控制从父线程中继承过来的value的初始值。在SimpleInheritableThreadLocal中会对从父线程继承的Long对象做加1处理。默认情况下,是不做任何处理的。SimpleInheritableThreadLocal的代码如下:
[java] view plain copy
-
public class SimpleInheritableThreadLocal<T> extends InheritableThreadLocal<T> {
-
-
@Override
-
protected T childValue(T parentValue) {
-
// TODO Auto-generated method stub
-
if (parentValue instanceof Long) {
-
Long res = (Long) parentValue + 1;
-
return (T) res;
-
}
-
-
return super.childValue(parentValue);
-
}
-
}
可能有人会有疑问:ThreadLocal是线程隔离的,而InheritableThreadLocal又是ThreadLocal的子类,所以InheritableThreadLocal也应该是线程隔离的。那这里是怎么做到从父线程继承值呢?子线想一想:实现线程隔离的核心思想是将要隔离的变量保存到Thread对象里面,然后通过ThreadLocal来读写这个变量;其实不通过ThreadLocal,在Thread内部是可以直接读写threadLocals的。那既然inheritableThreadLocals是Thread的成员变量,那么在初始化Thread的时候就可以直接读取父线程的inheritableThreadLocals并把它保存的values设置为子线程的inheritableThreadLocals初始值。
这个想法对不对呢?我们来看一下Thread初始化部分的源码:
[java] view plain copy
-
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");
-
}
-
-
this.name = name.toCharArray();
-
-
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();
-
if (security == null || isCCLOverridden(parent.getClass()))
-
this.contextClassLoader = parent.getContextClassLoader();
-
else
-
this.contextClassLoader = parent.contextClassLoader;
-
this.inheritedAccessControlContext =
-
acc != null ? acc : 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();
-
}
先看Thread的构造函数,这里调用了init函数,最终会调用这init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc)函数。直接看第60行,如果父线程的parent.inheritableThreadLocals不为null,就会以父线程的inheritableThreadLocals为参数来构造子线程的inheritableThreadLocals。我们再看一下createInheritedMap函数:
[java] view plain copy
-
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) {
-
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的构造函数中会遍历保存父线程的inheritableThreadLocals中键值对,并且在读取value的会调用key.childValue计算子线程中的value。所以在SimpleInheritableThreadLocal重写了childValue来根据父线程的value计算子线程的value,默认情况是不做任何处理,直接返回的,如:
[java] view plain copy
-
T childValue(T parentValue) {
-
throw new UnsupportedOperationException();
-
}