一、需求介紹
由于項目需要在同一程序不同方法内擷取到上下文内容,而在同一線程父方法可以擷取到上下文内容,子方法由不同的項目組提供和開發,然後以jar包的方式打包,這時候問題就出現了,父方法的上下文内容如何可以提供給子方法,并在子方法中擷取到對應線程的上下文内容?
注:每個線程的上下文内容是不同的,父類隻提供內建容器,具體方法的實作由不同的項目組和部門編寫
二、解決思路
應用Java 提供的ThreadLocal
首先檢視下TheadLocal提供的方法
T get()
傳回此線程局部變量的目前線程副本中的值。
protected T initialValue()
傳回此線程局部變量的目前線程的初始值。
void remove()
移除此線程局部變量的值。
void set(T value)
将此線程局部變量的目前線程副本中的值設定為指定值。
2.1、get 方法:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
說明:
1、首先擷取目前線程
2、擷取目前線程的ThreadLocalMap即threadLocals
getMap(Thread t)源碼為:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
3、如果目前線程存在threadLocals則根據目前的ThreadLocal擷取threadLocals的值.(threadLocals其實就是一Map)
4、如果目前線程的ThreadLocalMap為null,則執行方法setInitialValue()
可以看的出是擷取Thread的threadLocals
Thread中源碼為如下:
ThreadLocal.ThreadLocalMap threadLocals = null;
2.2、set 方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
源碼說明:
1、首先擷取目前線程
2、擷取目前線程ThreadLocalMap
3、如果ThreadLocalMap不為null set(ThreadLocal,value)
4、如果為null createMap(Thread,value);
備注:這裡可以看出不為null時,set的内容為key:ThreadLocal,value:value,也就是說每個線程都有一個ThreadLocalMap這個map記憶體放得内容為ThreadLocal,value
接下來在看下createMap源碼
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
備注:該方法為目前線程的内部變量threadLocals進行了指派操作,而ThreadLocalMap為ThreadLocal的内部類。new ThreadLocalMap(this, firstValue) 内部進行的操作也是為目前線程的threadLocals變量進行了set操作,key為目前的ThreadLocal,value為傳入的firstValue
2.3、setInitialValue方法
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
該方法傳回null,并會建立目前線程的threadLocals
綜上:應用ThreadLocal線上程中進行上下文同步時,要保持一緻的ThreadLocal。當多線程應用線程池時,要保證線程池中相同的線程每次拿到得ThreadLocal要不相同,否則會出現問題。
回到問題的開始,解決該問題的方案為
1、建立一個單例類(ex:ThreadLocalTest)保證線程上下文拿到得ThreadLocal都一緻
2、父類提供給子類可以擷取單例類ThreadLocalTest的方法。(要是子類分别由不同的部門、項目組以jar的方式提供時,負責父類服務的可以給項目組提供一個通用的jar檔案)
3、在調用父類多線程入口時擷取ThreadLocalTest單例,set(上下文内容)
4、子類在方法内擷取ThreadLocalTest單例調用get()方法擷取目前線程的value即為上下文内容
5、父類調用子方法後執行ThreadLocalTest.remove()操作
public class ThreadLocalTest<T> {
private static ThreadLocal local = new ThreadLocal();
private static ThreadLocalTest threadlocaltest = null;
public synchronized ThreadLocalTest getInitialize(){
if(threadlocaltest == null){
threadlocaltest = new ThreadLocalTest<T>();
}
return threadlocaltest;
}
public T get(){
return (T) local.get();
}
public void set(T t){
local.set(t);
}
public void remove(){
local.remove();
}
}
後記:
很多人問我,在父類中既然可以擷取到上下文那直接傳給子類方法就行了啊。注意前提說的是父類隻負責提供一個容器,具體的實作由不同的部門和項目組編寫