什麼是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++;//統計增加
}
}
}
}
是不是非常簡單!!!