log(二)——MDC实现之ThreadLocal
?因為MDC底層是用ThreadLocal實現的,所以這里補充一些和ThreadLocal相關的知識點。
1.ThreadLocal的三個層次
關于ThreadLocal有三個層次,可以按照這三個層次去理解就不會亂。
三個層次
?* 第一層是Thread空間,通過Thread.currentThread()獲得。
?* 第二層是Thread中的兩個ThreadLocalMap,threadLocals和inheritableThreadLocals,訪問thread對應的兩個ThreadLocalMap成員變量獲得。
?* 第三層是每個ThreadLocalMap中key——ThreadLocal和value——ThreadLocal的set方法set的值,在get方法中用ThreadLocal的this作為ThreadLocalMap的key獲取value。
?* 無論什么操作都要按照這三個層次依次進行才不會亂
2.ThreadLocalMap
保存了當前Thread中存放的ThreadLocal和ThreadLocal對應的值的鍵值對。
當前線程的所有ThreadLocal變量組成了這個map的keyset,對應的值組成了這個map的valueset。
3.ThreadLocal的操作
第一步都是先用Thread.currentThread()獲得當前線程,然后getMap獲取線程中的ThreadLocalMap。
像getMap這些操作方法都是包可見性的,包外部無法操作。以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類中有兩個ThreadLocalMap,一個是threadLocals,一個是inheritableThreadLocals。
threadLocals保存的是當前線程中的ThreadLocal變量們,inheritableThreadLocals保存的是當前線程父線程中的變量們。
InheritableThreadLocal類覆寫了getMap和createMap這兩個方法,
/*** 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的時候,獲取到的都是inheritableThreadLocals引用,操作的也是inheritableThreadLocals這個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一個Thread的時候會調用Thread的init方法,該方法中如果parent線程的inheritableThreadLocals不是null的話,就會用createInheritedMap方法,用parent的inheritableThreadLocals中的元素構造一個新的ThreadLocalMap。
注意:該操作只在線程初始化的時候進行,所以在該線程初始化之后,parent線程對parent線程自己的inheritableThreadLocals變量的操作不會影響到當前線程的inheritableThreadLocals了,因為已經不是同一個map了。
MDC就是利用這個InheritableThreadLocal把父線程的context帶到子線程中,把上下文傳遞到子線程中通過日志輸出,把一次完整的請求串聯起來。
6.parent線程
Thread parent = currentThread();在Thread的init方法中,是通過獲得當前線程作為parent線程,也就是說,在哪個線程中new的這個Thread并start的,執行該操作的線程就是new的新Thread的parent線程。
但是init方法只在線程初始化的時候執行一次,所以如果用的線程池來使線程重用的話,就不會再調用這個init方法了,這會帶來一些問題,后面會具體說。
?
總結
以上是生活随笔為你收集整理的log(二)——MDC实现之ThreadLocal的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 喜欢听音乐的小伙伴看过来
- 下一篇: C#高斯平滑算法 :二维高斯卷积代码实例