什么是InheritableThreadLocal
上一篇文章已经对ThreadLocal的分析做了详细讲解请参考TheadLocal一文让你读懂
那么我们知道了什么是ThreadLocal,接下来理解InheritableThreadLocal就容易多了,首先InheritableThreadLocal是对ThreadLocal的扩展和继承,它的数据ThreadLocal.ThreadLocalMap保存在Thread的inheritableThreadLocals变量中,同时如果我们在当前线程开启一个新线程,同时如果主线程存在inheritableThreadLocals那么子线程会copy一份主线程中的这个变量持有值,In the other world!当主线程持有inheritableThreadLocals时且同时开启一个新线程,新线程会复制一份inheritableThreadLocals到子线程的inheritableThreadLocals中,类似子线程继承了父线程的inheritableThreadLocals。
InheritableThreadLocal使用场景
入门示例
package com.ouwen.springboot.juc;
import java.util.concurrent.TimeUnit;
/**
* @author <a href="http://youngitman.tech">青年IT男</a>
* @version v1.0.0
* @className InheritableThreadLocalTest
* @description
* @date 2020-01-30 10:56
* @JunitTest: {@link }
**/
public class InheritableThreadLocalTest {
public static void main(String[] args) throws InterruptedException {
//在threadmain线程绑定
InheritableThreadLocal inheritableThreadLocal = new InheritableThreadLocal();
testForInheritableThreadLocal(inheritableThreadLocal);
TimeUnit.MILLISECONDS.sleep(10_000);//jvm在所有非Daemon线程退出后停止
inheritableThreadLocal.remove();
}
/***
*
* InheritableThreadLocal使用
*
* @author liyong
* @date 00:42 2020-01-30
* * @param
* @exception
* @return void
**/
private static void testForInheritableThreadLocal(ThreadLocal threadLocal) {
testMain(threadLocal);
//子线程复制ThreadLocalMap到inheritableThreadLocal
new Thread(new ThreadGroup("InheritableGroup"), () -> {
System.out.println("The child thread's inheritableThreadLocals is " + threadLocal.get());
}, "InheritableThreadLocal", 10, true).start();
}
/***
*
* 测试主线程
*
* @author liyong
* @date 11:45 2020-01-29
* * @param threadLocal
* @exception
* @return void
**/
private static void testMain(ThreadLocal threadLocal) {
System.out.println("The thread" + Thread.currentThread().getName() + " index number is " + threadLocal.get() + " at before");
threadLocal.set("`mainindex`");
System.out.println("The thread" + Thread.currentThread().getName() + " index number is " + threadLocal.get() + " at after ");
}
}
从上面代码testMain()
方法设置了主线程的inheritableThreadLocals变量的值,testForInheritableThreadLocal()
方法开启了新线程,同时打印了子线程的InheritableThreadLocal持有的值,执行结果如下:
The threadmain index number is null at before
The threadmain index number is `mainindex` at after
The child thread's inheritableThreadLocals is `mainindex`
看到子线程打印的值mainindex
和主线程设置的值mainindex
一致说明,子线程copy了主线程中的inheritableThreadLocals的值。
Spring的RequestContextHolder使用
在Spring中有如下实现:
这里我们重点看下NamedInheritableThreadLocal以及使用的场景。从实现代码可以看出这里只增加了一个name
属性,看下在Spring中的使用org.springframework.web.context.request.RequestContextHolder
:
从这里可以看出我们在SpringWeb开发中常常使用**request.setAttribute(key, value)**去设置一个requst
范围的值进行数据传递,并且这个scope是线程隔离。从org.springframework.web.context.request.RequestContextListener
可以知道当有请求事件到达的时候需要将javax.servlet.http.HttpServletRequest
中的值进行传递
InheritableThreadLocal源码分析
下面我们一起来通过上面的入门示例分析ThreadLocal源码
源码分析
InheritableThreadLocal
的实例化和get()
、set()
和ThreadLocal
一样,InheritableThreadLocal
重新了几个方法如下:
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);
}
}
代码很简单重新父类的getMap()
和createMap()
,getMap()
方法返回的是Thread
的inheritableThreadLocals
变量值、createMap()
在创建ThreadLocalMap时把对象值赋值给Thread
的inheritableThreadLocals
变量值。
线程创建过程
根据我们在上面的入门示例跟踪代码如下:
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize, boolean inheritThreadLocals) {
init(group, target, name, stackSize, null, inheritThreadLocals);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
//当前线程(父线程)
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);
//重点在这里,当inheritThreadLocals==true且parent.inheritableThreadLocals存在值的时候就把这个值复制到this.inheritableThreadLocals
if (inheritThreadLocals && 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();
}
接下来看下java.lang.ThreadLocal#createInheritedMap
代码
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
看下ThreadLocalMap
是怎么复制的java.lang.ThreadLocal.ThreadLocalMap#ThreadLocalMap(java.lang.ThreadLocal.ThreadLocalMap)
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (Entry e : parentTable) {
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {//没有被gc回收
Object value = key.childValue(e.value);//InheritableThreadLocal简单实现直接返回值
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)//通过线性探测找到数组中值为null的索引
h = nextIndex(h, len);
table[h] = c;//设置当前位置值
size++;//统计增加
}
}
}
}
是不是非常简单!!!