log(二)——MDC实现之ThreadLocal
?因?yàn)镸DC底層是用ThreadLocal實(shí)現(xiàn)的,所以這里補(bǔ)充一些和ThreadLocal相關(guān)的知識(shí)點(diǎn)。
1.ThreadLocal的三個(gè)層次
關(guān)于ThreadLocal有三個(gè)層次,可以按照這三個(gè)層次去理解就不會(huì)亂。
三個(gè)層次
?* 第一層是Thread空間,通過(guò)Thread.currentThread()獲得。
?* 第二層是Thread中的兩個(gè)ThreadLocalMap,threadLocals和inheritableThreadLocals,訪問(wèn)thread對(duì)應(yīng)的兩個(gè)ThreadLocalMap成員變量獲得。
?* 第三層是每個(gè)ThreadLocalMap中key——ThreadLocal和value——ThreadLocal的set方法set的值,在get方法中用ThreadLocal的this作為T(mén)hreadLocalMap的key獲取value。
?* 無(wú)論什么操作都要按照這三個(gè)層次依次進(jìn)行才不會(huì)亂
2.ThreadLocalMap
保存了當(dāng)前Thread中存放的ThreadLocal和ThreadLocal對(duì)應(yīng)的值的鍵值對(duì)。
當(dāng)前線程的所有ThreadLocal變量組成了這個(gè)map的keyset,對(duì)應(yīng)的值組成了這個(gè)map的valueset。
3.ThreadLocal的操作
第一步都是先用Thread.currentThread()獲得當(dāng)前線程,然后getMap獲取線程中的ThreadLocalMap。
像getMap這些操作方法都是包可見(jiàn)性的,包外部無(wú)法操作。以ThreadLocal的get方法舉例,
/*** Returns the value in the current thread's copy of this* thread-local variable. If the variable has no value for the* current thread, it is first initialized to the value returned* by an invocation of the {@link #initialValue} method.** @return the current thread's value of this thread-local*/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();}4.InheritableThreadLocal
Thread類中有兩個(gè)ThreadLocalMap,一個(gè)是threadLocals,一個(gè)是inheritableThreadLocals。
threadLocals保存的是當(dāng)前線程中的ThreadLocal變量們,inheritableThreadLocals保存的是當(dāng)前線程父線程中的變量們。
InheritableThreadLocal類覆寫(xiě)了getMap和createMap這兩個(gè)方法,
/*** 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.* @param map the map to store.*/void createMap(Thread t, T firstValue) {t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);}可以看出在初始化或者getMap的時(shí)候,獲取到的都是inheritableThreadLocals引用,操作的也是inheritableThreadLocals這個(gè)ThreadLocalMap。
5.threadLocals和inheritableThreadLocals的初始化
/*** Initializes a Thread.** @param g the Thread group* @param target the object whose run() method gets called* @param name the name of the new Thread* @param stackSize the desired stack size for the new thread, or* zero to indicate that this parameter is to be ignored.*/private void init(ThreadGroup g, Runnable target, String name,long stackSize) {...Thread parent = currentThread();...if (parent.inheritableThreadLocals != null)this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);...}在new一個(gè)Thread的時(shí)候會(huì)調(diào)用Thread的init方法,該方法中如果parent線程的inheritableThreadLocals不是null的話,就會(huì)用createInheritedMap方法,用parent的inheritableThreadLocals中的元素構(gòu)造一個(gè)新的ThreadLocalMap。
注意:該操作只在線程初始化的時(shí)候進(jìn)行,所以在該線程初始化之后,parent線程對(duì)parent線程自己的inheritableThreadLocals變量的操作不會(huì)影響到當(dāng)前線程的inheritableThreadLocals了,因?yàn)橐呀?jīng)不是同一個(gè)map了。
MDC就是利用這個(gè)InheritableThreadLocal把父線程的context帶到子線程中,把上下文傳遞到子線程中通過(guò)日志輸出,把一次完整的請(qǐng)求串聯(lián)起來(lái)。
6.parent線程
Thread parent = currentThread();在Thread的init方法中,是通過(guò)獲得當(dāng)前線程作為parent線程,也就是說(shuō),在哪個(gè)線程中new的這個(gè)Thread并start的,執(zhí)行該操作的線程就是new的新Thread的parent線程。
但是init方法只在線程初始化的時(shí)候執(zhí)行一次,所以如果用的線程池來(lái)使線程重用的話,就不會(huì)再調(diào)用這個(gè)init方法了,這會(huì)帶來(lái)一些問(wèn)題,后面會(huì)具體說(shuō)。
?
總結(jié)
以上是生活随笔為你收集整理的log(二)——MDC实现之ThreadLocal的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 喜欢听音乐的小伙伴看过来
- 下一篇: 协众信息技术平面海报设计,你知道多少